/* keyedit.c - Edit properties of a key
 * Copyright (C) 1998-2010 Free Software Foundation, Inc.
 * Copyright (C) 1998-2017 Werner Koch
 * Copyright (C) 2015, 2016, 2022-2023 g10 Code GmbH
 *
 * This file is part of GnuPG.
 *
 * GnuPG is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * GnuPG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * 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 <https://www.gnu.org/licenses/>.
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#ifdef HAVE_LIBREADLINE
# define GNUPG_LIBREADLINE_H_INCLUDED
# include <readline/readline.h>
#endif

#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "keydb.h"
#include "photoid.h"
#include "../common/util.h"
#include "main.h"
#include "trustdb.h"
#include "filter.h"
#include "../common/ttyio.h"
#include "../common/status.h"
#include "../common/i18n.h"
#include "../common/mbox-util.h"
#include "keyserver-internal.h"
#include "call-agent.h"
#include "../common/host2net.h"
#include "tofu.h"
#include "key-check.h"
#include "key-clean.h"
#include "keyedit.h"

static void maybe_upload_key (ctrl_t ctrl, kbnode_t keyblock);
static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig,
			int verbose);
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,
				     int with_revoker, int with_fpr,
				     int with_subkeys, int with_prefs,
                                     int nowarn);
static void show_key_and_fingerprint (ctrl_t ctrl,
                                      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 (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 (ctrl_t ctrl, kbnode_t pub_keyblock);
static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, unsigned int options);
static void menu_delkey (KBNODE pub_keyblock);
static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive);
static int menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock,
                         const char *adskfpr);
static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock,
                                int unattended, u32 newexpiration);
static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock);
static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock);
static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock);
static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock,
                                 int unattended);
static int menu_set_keyserver_url (ctrl_t ctrl,
                                   const char *url, kbnode_t pub_keyblock);
static int menu_set_notation (ctrl_t ctrl,
                              const char *string, kbnode_t 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, 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_selected_uids (KBNODE keyblock);
static int real_uids_left (KBNODE keyblock);
static int count_selected_keys (KBNODE keyblock);
static int menu_revsig (ctrl_t ctrl, kbnode_t 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 (ctrl_t ctrl, kbnode_t pub_keyblock);
static int menu_revsubkey (ctrl_t ctrl, kbnode_t pub_keyblock);
#ifndef NO_TRUST_MODELS
static int enable_disable_key (ctrl_t ctrl, kbnode_t keyblock, int disable);
#endif /*!NO_TRUST_MODELS*/
static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock);

static int update_trust = 0;

#define CONTROL_D ('D' - 'A' + 1)

/* Flags values used by sign_uids().  */
#define SIGN_UIDS_LOCAL         1  /* Create non-exportable sig.    */
#define SIGN_UIDS_NONREVOCABLE  2  /* Create non-revocable sig.     */
#define SIGN_UIDS_TRUSTSIG      4  /* Create trust signature.       */
#define SIGN_UIDS_INTERACTIVE   8  /* Change the way of prompting.  */
#define SIGN_UIDS_QUICK        16  /* Called by a --quick command.  */

struct sign_attrib
{
  int non_exportable, non_revocable;
  struct revocation_reason_info *reason;
  byte trust_depth, trust_value;
  char *trust_regexp;
};



/* TODO: Fix duplicated code between here and the check-sigs/list-sigs
   code in keylist.c. */
static int
print_and_check_one_sig_colon (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node,
			       int *inv_sigs, int *no_key, int *oth_err,
			       int *is_selfsig, int print_without_key)
{
  PKT_signature *sig = node->pkt->pkt.signature;
  int rc, sigrc;

  /* 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 (ctrl, keyblock, node, is_selfsig);
  switch (gpg_err_code (rc))
    {
    case 0:
      node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR);
      sigrc = '!';
      break;
    case GPG_ERR_BAD_SIGNATURE:
      node->flag = NODFLG_BADSIG;
      sigrc = '-';
      if (inv_sigs)
	++ * inv_sigs;
      break;
    case GPG_ERR_NO_PUBKEY:
    case GPG_ERR_UNUSABLE_PUBKEY:
      node->flag = NODFLG_NOKEY;
      sigrc = '?';
      if (no_key)
	++ * no_key;
      break;
    default:
      node->flag = NODFLG_SIGERR;
      sigrc = '%';
      if (oth_err)
	++ * oth_err;
      break;
    }

  if (sigrc != '?' || print_without_key)
    {
      es_printf ("sig:%c::%d:%08lX%08lX:%lu:%lu:",
                 sigrc, sig->pubkey_algo, (ulong) sig->keyid[0],
                 (ulong) sig->keyid[1], (ulong) sig->timestamp,
                 (ulong) sig->expiredate);

      if (sig->trust_depth || sig->trust_value)
	es_printf ("%d %d", sig->trust_depth, sig->trust_value);

      es_printf (":");

      if (sig->trust_regexp)
	es_write_sanitized (es_stdout,
			    sig->trust_regexp, strlen (sig->trust_regexp),
			    ":", NULL);

      es_printf ("::%02x%c\n", sig->sig_class,
                 sig->flags.exportable ? 'x' : 'l');

      if (opt.show_subpackets)
	print_subpackets_colon (sig);
    }

  return (sigrc == '!');
}


/*
 * 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.
 */
int
keyedit_print_one_sig (ctrl_t ctrl, estream_t fp,
                       int rc, kbnode_t keyblock, kbnode_t 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;
  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 */

  switch (gpg_err_code (rc))
    {
    case 0:
      node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR);
      sigrc = '!';
      break;
    case GPG_ERR_BAD_SIGNATURE:
      node->flag = NODFLG_BADSIG;
      sigrc = '-';
      if (inv_sigs)
	++ * inv_sigs;
      break;
    case GPG_ERR_NO_PUBKEY:
    case GPG_ERR_UNUSABLE_PUBKEY:
      node->flag = NODFLG_NOKEY;
      sigrc = '?';
      if (no_key)
	++ * no_key;
      break;
    default:
      node->flag = NODFLG_SIGERR;
      sigrc = '%';
      if (oth_err)
	++ * oth_err;
      break;
    }
  if (sigrc != '?' || print_without_key)
    {
      tty_fprintf (fp, "%s%c%c %c%c%c%c%c%c %s %s",
		  is_rev ? "rev" : "sig", sigrc,
		  (sig->sig_class - 0x10 > 0 &&
		   sig->sig_class - 0x10 <
		   4) ? '0' + sig->sig_class - 0x10 : ' ',
		  sig->flags.exportable ? ' ' : 'L',
		  sig->flags.revocable ? ' ' : 'R',
		  sig->flags.policy_url ? 'P' : ' ',
		  sig->flags.notation ? 'N' : ' ',
		  sig->flags.expired ? 'X' : ' ',
		  (sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
						  0) ? '0' +
		  sig->trust_depth : ' ',
                  keystr (sig->keyid),
		  datestr_from_sig (sig));
      if ((opt.list_options & LIST_SHOW_SIG_EXPIRE) || extended )
	tty_fprintf (fp, " %s", expirestr_from_sig (sig));
      tty_fprintf (fp, "  ");
      if (sigrc == '%')
	tty_fprintf (fp, "[%s] ", gpg_strerror (rc));
      else if (sigrc == '?')
	;
      else if (is_selfsig)
	{
	  tty_fprintf (fp, is_rev ? _("[revocation]") : _("[self-signature]"));
          if (extended && sig->flags.chosen_selfsig)
            tty_fprintf (fp, "*");
	}
      else
	{
	  size_t n;
	  char *p = get_user_id (ctrl, sig->keyid, &n, NULL);
	  tty_print_utf8_string2 (fp, p, n,
				  opt.screen_columns - keystrlen () - 26 -
				  ((opt.
				    list_options & LIST_SHOW_SIG_EXPIRE) ? 11
				   : 0));
	  xfree (p);
	}
      if (fp == log_get_stream ())
        log_printf ("\n");
      else
        tty_fprintf (fp, "\n");

      if (sig->flags.policy_url
          && ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended))
	show_policy_url (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0));

      if (sig->flags.notation
          && ((opt.list_options & LIST_SHOW_NOTATIONS) || extended))
	show_notation (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0),
		       ((opt.
			 list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) +
		       ((opt.
			 list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0) +
                       ((opt.
                         list_options & LIST_SHOW_HIDDEN_NOTATIONS) ? 4:0));

      if (sig->flags.pref_ks
          && ((opt.list_options & LIST_SHOW_KEYSERVER_URLS) || extended))
	show_keyserver_url (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0));

      if (extended)
        {
          PKT_public_key *pk = keyblock->pkt->pkt.public_key;
          const unsigned char *s;

          s = parse_sig_subpkt (sig, 1, SIGSUBPKT_PRIMARY_UID, NULL);
          if (s && *s)
            tty_fprintf (fp, "             [primary]\n");

          s = parse_sig_subpkt (sig, 1, SIGSUBPKT_KEY_EXPIRE, NULL);
          if (s && buf32_to_u32 (s))
            tty_fprintf (fp, "             [expires: %s]\n",
                         isotimestamp (pk->timestamp + buf32_to_u32 (s)));
        }
    }

  return (sigrc == '!');
}


static int
print_and_check_one_sig (ctrl_t ctrl, kbnode_t keyblock, kbnode_t 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 (ctrl, keyblock, node, is_selfsig);
  return keyedit_print_one_sig (ctrl, NULL, rc,
				keyblock, node, inv_sigs, no_key, oth_err,
				*is_selfsig, print_without_key, extended);
}


static int
sign_mk_attrib (PKT_signature * sig, void *opaque)
{
  struct sign_attrib *attrib = opaque;
  byte buf[8];

  if (attrib->non_exportable)
    {
      buf[0] = 0;		/* not exportable */
      build_sig_subpkt (sig, SIGSUBPKT_EXPORTABLE, buf, 1);
    }

  if (attrib->non_revocable)
    {
      buf[0] = 0;		/* not revocable */
      build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1);
    }

  if (attrib->reason)
    revocation_reason_build_cb (sig, attrib->reason);

  if (attrib->trust_depth)
    {
      /* Not critical.  If someone doesn't understand trust sigs,
         this can still be a valid regular signature. */
      buf[0] = attrib->trust_depth;
      buf[1] = attrib->trust_value;
      build_sig_subpkt (sig, SIGSUBPKT_TRUST, buf, 2);

      /* Critical.  If someone doesn't understands regexps, this
         whole sig should be invalid.  Note the +1 for the length -
         regexps are null terminated. */
      if (attrib->trust_regexp)
	build_sig_subpkt (sig, SIGSUBPKT_FLAG_CRITICAL | SIGSUBPKT_REGEXP,
			  attrib->trust_regexp,
			  strlen (attrib->trust_regexp) + 1);
    }

  return 0;
}


/* Parse a trust signature specification string into the 3 return
 * args.  Returns 0 on success or an errorcode.  Format for the string
 * is
 *  ['T=']<depth>,<value>[,<domain>]
 * The optional prefix is just to allow c+p from the --check-sigs
 * output.  The domain is optional, <depth> must be a value in the
 * range 0 to 255, value may either be value in the same range or -
 * preferred - 'm' or 'f'.
 */
static gpg_error_t
parse_trustsig_string (const char *string,
                       byte *trust_value, byte *trust_depth, char **regexp)
{
  gpg_error_t err = 0;
  char **fields;
  int nfields;
  int along;
  char *endp;

  *trust_value = 0;
  *trust_depth = 0;
  *regexp = NULL;

  if (!string)
    return gpg_error (GPG_ERR_INV_ARG);

  if (*string == 'T' && string[1] == '=')
    string += 2;

  fields = strtokenize (string, ",");
  if (!fields)
      return gpg_error_from_syserror ();

  for (nfields=0; fields[nfields]; nfields++)
    ;
  if (nfields < 2 || nfields > 3)
    {
      err = gpg_error (GPG_ERR_SYNTAX);
      goto leave;
    }
  along = strtol (fields[0], &endp, 10);
  if (along < 0 || along > 255 || fields[0] == endp || *endp)
    {
      err = gpg_error (GPG_ERR_ERANGE);
      goto leave;
    }
  *trust_depth = along;
  if (!strcmp (fields[1], "m")|| !strcmp (fields[1], "marginal"))
    along = 60;
  else if (!strcmp (fields[1], "f")|| !strcmp (fields[1], "full"))
    along = 120;
  else
    {
      along = strtol (fields[1], &endp, 10);
      if (along < 0 || along > 255 || fields[1] == endp || *endp)
        {
          err = gpg_error (GPG_ERR_ERANGE);
          goto leave;
        }
    }
  *trust_value = along;

  if (nfields == 3)
    {
      if (!is_valid_domain_name (fields[2]))
        err = gpg_error (GPG_ERR_NO_NAME);
      else
        {
          *regexp = strconcat ("<[^>]+[@.]", fields[2], ">$", NULL);
          if (!*regexp)
            err = gpg_error_from_syserror ();
        }
    }

 leave:
  xfree (fields);
  if (err && *regexp)
    {
      xfree (*regexp);
      *regexp = NULL;
    }
  return err;
}


/* Interactive version of parse_trustsig_string.  */
static void
trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp)
{
  char *p;

  *trust_value = 0;
  *trust_depth = 0;
  *regexp = NULL;

  /* Same string as pkclist.c:do_edit_ownertrust */
  tty_printf (_
	      ("Please decide how far you trust this user to correctly verify"
	       " other users' keys\n(by looking at passports, checking"
	       " fingerprints from different sources, etc.)\n"));
  tty_printf ("\n");
  tty_printf (_("  %d = I trust marginally\n"), 1);
  tty_printf (_("  %d = I trust fully\n"), 2);
  tty_printf ("\n");

  while (*trust_value == 0)
    {
      p = cpr_get ("trustsig_prompt.trust_value", _("Your selection? "));
      trim_spaces (p);
      cpr_kill_prompt ();
      /* 60 and 120 are as per RFC2440 */
      if (p[0] == '1' && !p[1])
	*trust_value = 60;
      else if (p[0] == '2' && !p[1])
	*trust_value = 120;
      xfree (p);
    }

  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"
		"trust signatures on your behalf.\n"));
  tty_printf ("\n");

  while (*trust_depth == 0)
    {
      p = cpr_get ("trustsig_prompt.trust_depth", _("Your selection? "));
      trim_spaces (p);
      cpr_kill_prompt ();
      *trust_depth = atoi (p);
      xfree (p);
    }

  tty_printf ("\n");

  tty_printf (_("Please enter a domain to restrict this signature, "
		"or enter for none.\n"));

  tty_printf ("\n");

  p = cpr_get ("trustsig_prompt.trust_regexp", _("Your selection? "));
  trim_spaces (p);
  cpr_kill_prompt ();

  if (strlen (p) > 0)
    {
      char *q = p;
      int regexplen = 100, ind;

      *regexp = xmalloc (regexplen);

      /* Now mangle the domain the user entered into a regexp.  To do
         this, \-escape everything that isn't alphanumeric, and attach
         "<[^>]+[@.]" to the front, and ">$" to the end. */

      strcpy (*regexp, "<[^>]+[@.]");
      ind = strlen (*regexp);

      while (*q)
	{
	  if (!((*q >= 'A' && *q <= 'Z')
		|| (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9')))
	    (*regexp)[ind++] = '\\';

	  (*regexp)[ind++] = *q;

	  if ((regexplen - ind) < 3)
	    {
	      regexplen += 100;
	      *regexp = xrealloc (*regexp, regexplen);
	    }

	  q++;
	}

      (*regexp)[ind] = '\0';
      strcat (*regexp, ">$");
    }

  xfree (p);
  tty_printf ("\n");
}


/*
 * 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.  FLAGS are the SIGN_UIDS_* constants.
 * For example with SIGN_UIDS_QUICK the function won't ask the user
 * and use sensible defaults.  TRUSTSIGSTR is only used if also
 * SIGN_UIDS_TRUSTSIG is set.
 */
static int
sign_uids (ctrl_t ctrl, estream_t fp,
           kbnode_t keyblock, strlist_t locusr, unsigned int flags,
           const char *trustsigstr, int *ret_modified)
{
  int rc = 0;
  SK_LIST sk_list = NULL;
  SK_LIST sk_rover = NULL;
  PKT_public_key *pk = NULL;
  KBNODE node, uidnode;
  PKT_public_key *primary_pk = NULL;
  char *trust_regexp = NULL;
  int select_all = (!count_selected_uids (keyblock)
                    || (flags & SIGN_UIDS_INTERACTIVE));


  /* Build a list of all signators.
   *
   * We use the CERT flag to request the primary which must always
   * be one which is capable of signing keys.  I can't see a reason
   * 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 (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT);
  if (rc)
    goto leave;

  /* Loop over all signators.  */
  for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
    {
      u32 sk_keyid[2], pk_keyid[2];
      char *p;
      int class = 0, selfsig = 0;
      u32 duration = 0, timestamp = 0;
      byte trust_depth = 0, trust_value = 0;

      pk = sk_rover->pk;
      keyid_from_pk (pk, sk_keyid);

      /* Set mark A for all selected user ids.  */
      for (node = keyblock; node; node = node->next)
	{
	  if (select_all || (node->flag & NODFLG_SELUID))
	    node->flag |= NODFLG_MARK_A;
	  else
	    node->flag &= ~NODFLG_MARK_A;
	}

      /* Reset mark for uids which are already signed.  */
      uidnode = NULL;
      for (node = keyblock; node; node = node->next)
	{
	  if (node->pkt->pkttype == PKT_PUBLIC_KEY)
	    {
	      primary_pk = node->pkt->pkt.public_key;
	      keyid_from_pk (primary_pk, pk_keyid);

	      /* Is this a self-sig? */
	      if (pk_keyid[0] == sk_keyid[0] && pk_keyid[1] == sk_keyid[1])
                selfsig = 1;
	    }
	  else if (node->pkt->pkttype == PKT_USER_ID)
	    {
	      uidnode = (node->flag & NODFLG_MARK_A) ? node : NULL;
	      if (uidnode)
		{
		  int yesreally = 0;
		  char *user;

                  user = utf8_to_native (uidnode->pkt->pkt.user_id->name,
                                         uidnode->pkt->pkt.user_id->len, 0);

                  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);

		      if (selfsig)
			tty_fprintf (fp, "\n");
		      else if (opt.expert && !(flags & SIGN_UIDS_QUICK))
			{
			  tty_fprintf (fp, "\n");
			  /* No, so remove the mark and continue */
			  if (!cpr_get_answer_is_yes ("sign_uid.revoke_okay",
						      _("Are you sure you "
							"still want to sign "
							"it? (y/N) ")))
			    {
			      uidnode->flag &= ~NODFLG_MARK_A;
			      uidnode = NULL;
			    }
			  else if ((flags & SIGN_UIDS_INTERACTIVE))
			    yesreally = 1;
			}
		      else
			{
			  uidnode->flag &= ~NODFLG_MARK_A;
			  uidnode = NULL;
			  tty_fprintf (fp, _("  Unable to sign.\n"));
			}
		    }
		  else if (uidnode->pkt->pkt.user_id->flags.expired)
		    {
		      tty_fprintf (fp, _("User ID \"%s\" is expired."), user);

		      if (selfsig)
			tty_fprintf (fp, "\n");
		      else if (opt.expert && !(flags & SIGN_UIDS_QUICK))
			{
			  tty_fprintf (fp, "\n");
			  /* No, so remove the mark and continue */
			  if (!cpr_get_answer_is_yes ("sign_uid.expire_okay",
						      _("Are you sure you "
							"still want to sign "
							"it? (y/N) ")))
			    {
			      uidnode->flag &= ~NODFLG_MARK_A;
			      uidnode = NULL;
			    }
			  else if ((flags & SIGN_UIDS_INTERACTIVE))
			    yesreally = 1;
			}
		      else
			{
			  uidnode->flag &= ~NODFLG_MARK_A;
			  uidnode = NULL;
			  tty_fprintf (fp, _("  Unable to sign.\n"));
			}
		    }
		  else if (!uidnode->pkt->pkt.user_id->created && !selfsig)
		    {
		      tty_fprintf (fp, _("User ID \"%s\" is not self-signed."),
                                   user);

		      if (opt.expert && !(flags & SIGN_UIDS_QUICK))
			{
			  tty_fprintf (fp, "\n");
			  /* No, so remove the mark and continue */
			  if (!cpr_get_answer_is_yes ("sign_uid.nosig_okay",
						      _("Are you sure you "
							"still want to sign "
							"it? (y/N) ")))
			    {
			      uidnode->flag &= ~NODFLG_MARK_A;
			      uidnode = NULL;
			    }
			  else if ((flags & SIGN_UIDS_INTERACTIVE))
			    yesreally = 1;
			}
		      else
			{
			  uidnode->flag &= ~NODFLG_MARK_A;
			  uidnode = NULL;
			  tty_fprintf (fp, _("  Unable to sign.\n"));
			}
		    }

		  if (uidnode && (flags & SIGN_UIDS_INTERACTIVE)
                      && !yesreally && !(flags & SIGN_UIDS_QUICK))
		    {
		      tty_fprintf (fp,
                                   _("User ID \"%s\" is signable.  "), user);
		      if (!cpr_get_answer_is_yes ("sign_uid.sign_okay",
						  _("Sign it? (y/N) ")))
			{
			  uidnode->flag &= ~NODFLG_MARK_A;
			  uidnode = NULL;
			}
		    }

		  xfree (user);
		}
	    }
	  else if (uidnode && node->pkt->pkttype == PKT_SIGNATURE
		   && (node->pkt->pkt.signature->sig_class & ~3) == 0x10)
	    {
	      if (sk_keyid[0] == node->pkt->pkt.signature->keyid[0]
		  && sk_keyid[1] == node->pkt->pkt.signature->keyid[1])
		{
		  char buf[50];
		  char *user;

                  user = utf8_to_native (uidnode->pkt->pkt.user_id->name,
                                         uidnode->pkt->pkt.user_id->len, 0);

		  /* It's a v3 self-sig.  Make it into a v4 self-sig? */
		  if (node->pkt->pkt.signature->version < 4
                      && selfsig && !(flags & SIGN_UIDS_QUICK))
		    {
		      tty_fprintf (fp,
                                   _("The self-signature on \"%s\"\n"
                                     "is a PGP 2.x-style signature.\n"), user);

		      /* Note that the regular PGP2 warning below
		         still applies if there are no v4 sigs on
		         this key at all. */

		      if (opt.expert)
			if (cpr_get_answer_is_yes ("sign_uid.v4_promote_okay",
						   _("Do you want to promote "
						     "it to an OpenPGP self-"
						     "signature? (y/N) ")))
			  {
			    node->flag |= NODFLG_DELSIG;
			    xfree (user);
			    continue;
			  }
		    }

		  /* Is the current signature expired? */
		  if (node->pkt->pkt.signature->flags.expired)
		    {
		      tty_fprintf (fp, _("Your current signature on \"%s\"\n"
                                         "has expired.\n"), user);

		      if ((flags & SIGN_UIDS_QUICK) || cpr_get_answer_is_yes
			  ("sign_uid.replace_expired_okay",
			   _("Do you want to issue a "
			     "new signature to replace "
			     "the expired one? (y/N) ")))
			{
			  /* Mark these for later deletion.  We
			     don't want to delete them here, just in
			     case the replacement signature doesn't
			     happen for some reason.  We only delete
			     these after the replacement is already
			     in place. */

			  node->flag |= NODFLG_DELSIG;
			  xfree (user);
			  continue;
			}
		    }

		  if (!node->pkt->pkt.signature->flags.exportable
                      && !(flags & SIGN_UIDS_LOCAL))
		    {
		      /* It's a local sig, and we want to make a
		         exportable sig. */
		      tty_fprintf (fp, _("Your current signature on \"%s\"\n"
                                         "is a local signature.\n"), user);

		      if ((flags & SIGN_UIDS_QUICK) || cpr_get_answer_is_yes
			  ("sign_uid.local_promote_okay",
			   _("Do you want to promote "
			     "it to a full exportable " "signature? (y/N) ")))
			{
			  /* Mark these for later deletion.  We
			     don't want to delete them here, just in
			     case the replacement signature doesn't
			     happen for some reason.  We only delete
			     these after the replacement is already
			     in place. */

			  node->flag |= NODFLG_DELSIG;
			  xfree (user);
			  continue;
			}
		    }

		  /* Fixme: see whether there is a revocation in which
		   * case we should allow signing it again. */
		  if (!node->pkt->pkt.signature->flags.exportable
                      && (flags & SIGN_UIDS_LOCAL))
		    tty_fprintf ( fp,
                       _("\"%s\" was already locally signed by key %s\n"),
                       user, keystr_from_pk (pk));
		  else
		    tty_fprintf (fp,
                                _("\"%s\" was already signed by key %s\n"),
				user, keystr_from_pk (pk));

                  if (node->pkt->pkt.signature->digest_algo
                      == DIGEST_ALGO_SHA1
                      && !opt.flags.allow_weak_key_signatures)
                    {
                      /* Allow updating a signature to a stronger
                       * digest algorithm without an extra option.  */
		      xfree (user);
		      continue;
                    }
		  else if (opt.flags.force_sign_key
                      || (opt.expert && !(flags & SIGN_UIDS_QUICK)
                          && cpr_get_answer_is_yes ("sign_uid.dupe_okay",
                                                    _("Do you want to sign it "
                                                      "again anyway? (y/N) "))))
		    {
		      /* Don't delete the old sig here since this is
		         an --expert thing. */
		      xfree (user);
		      continue;
		    }

		  snprintf (buf, sizeof buf, "%08lX%08lX",
			    (ulong) pk->keyid[0], (ulong) pk->keyid[1]);
		  write_status_text (STATUS_ALREADY_SIGNED, buf);
		  uidnode->flag &= ~NODFLG_MARK_A;	/* remove mark */

		  xfree (user);
		}
	    }
	}

      /* Check whether any uids are left for signing.  */
      if (!count_uids_with_flag (keyblock, NODFLG_MARK_A))
	{
	  tty_fprintf (fp, _("Nothing to sign with key %s\n"),
		      keystr_from_pk (pk));
	  continue;
	}

      /* Ask whether we really should sign these user id(s). */
      tty_fprintf (fp, "\n");
      show_key_with_all_names (ctrl, fp, keyblock, 1, 0, 1, 0, 0, 0);
      tty_fprintf (fp, "\n");

      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)
	    {
	      tty_fprintf (fp, _("This key has expired!"));

	      if (opt.expert && !(flags & SIGN_UIDS_QUICK))
		{
		  tty_fprintf (fp, "  ");
		  if (!cpr_get_answer_is_yes ("sign_uid.expired_okay",
					      _("Are you sure you still "
						"want to sign it? (y/N) ")))
		    continue;
		}
	      else
		{
		  tty_fprintf (fp, _("  Unable to sign.\n"));
		  continue;
		}
	    }
	  else
	    {
	      tty_fprintf (fp, _("This key is due to expire on %s.\n"),
                           expirestr_from_pk (primary_pk));

	      if (opt.ask_cert_expire && !(flags & SIGN_UIDS_QUICK))
		{
		  char *answer = cpr_get ("sign_uid.expire",
					  _("Do you want your signature to "
					    "expire at the same time? (Y/n) "));
		  if (answer_is_yes_no_default (answer, 1))
		    {
		      /* This fixes the signature timestamp we're
		         going to make as now.  This is so the
		         expiration date is exactly correct, and not
		         a few seconds off (due to the time it takes
		         to answer the questions, enter the
		         passphrase, etc). */
		      timestamp = now;
		      duration = primary_pk->expiredate - now;
		    }

		  cpr_kill_prompt ();
		  xfree (answer);
		}
	    }
	}

      /* Only ask for duration if we haven't already set it to match
         the expiration of the pk */
      if (!duration && !selfsig)
	{
	  if (opt.ask_cert_expire && !(flags & SIGN_UIDS_QUICK))
	    duration = ask_expire_interval (1, opt.def_cert_expire);
	  else
	    duration = parse_expire_string (opt.def_cert_expire);
	}

      if (selfsig)
	;
      else
	{
	  if (opt.batch || !opt.ask_cert_level || (flags & SIGN_UIDS_QUICK))
	    class = 0x10 + opt.def_cert_level;
	  else
	    {
	      char *answer;

	      tty_fprintf (fp,
                           _("How carefully have you verified the key you are "
			    "about to sign actually belongs\nto the person "
			    "named above?  If you don't know what to "
			    "answer, enter \"0\".\n"));
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp, _("   (0) I will not answer.%s\n"),
			  opt.def_cert_level == 0 ? " (default)" : "");
	      tty_fprintf (fp, _("   (1) I have not checked at all.%s\n"),
			  opt.def_cert_level == 1 ? " (default)" : "");
	      tty_fprintf (fp, _("   (2) I have done casual checking.%s\n"),
			  opt.def_cert_level == 2 ? " (default)" : "");
	      tty_fprintf (fp,
                           _("   (3) I have done very careful checking.%s\n"),
			  opt.def_cert_level == 3 ? " (default)" : "");
	      tty_fprintf (fp, "\n");

	      while (class == 0)
		{
		  answer = cpr_get ("sign_uid.class",
                                    _("Your selection? "
                                      "(enter '?' for more information): "));
		  if (answer[0] == '\0')
		    class = 0x10 + opt.def_cert_level;	/* Default */
		  else if (ascii_strcasecmp (answer, "0") == 0)
		    class = 0x10;	/* Generic */
		  else if (ascii_strcasecmp (answer, "1") == 0)
		    class = 0x11;	/* Persona */
		  else if (ascii_strcasecmp (answer, "2") == 0)
		    class = 0x12;	/* Casual */
		  else if (ascii_strcasecmp (answer, "3") == 0)
		    class = 0x13;	/* Positive */
		  else
		    tty_fprintf (fp, _("Invalid selection.\n"));

		  xfree (answer);
		}
	    }

	  if ((flags & SIGN_UIDS_TRUSTSIG))
            {
              xfree (trust_regexp);
              trust_regexp = NULL;
              if ((flags & SIGN_UIDS_QUICK))
                {
                  rc = parse_trustsig_string (trustsigstr, &trust_value,
                                              &trust_depth, &trust_regexp);
                  if (rc)
                    goto leave;
                }
              else
                trustsig_prompt (&trust_value, &trust_depth, &trust_regexp);
            }
        }

      if (!(flags & SIGN_UIDS_QUICK))
        {
          p = get_user_id_native (ctrl, sk_keyid);
          tty_fprintf (fp,
                   _("Are you sure that you want to sign this key with your\n"
                     "key \"%s\" (%s)\n"), p, keystr_from_pk (pk));
          xfree (p);
        }

      if (selfsig)
	{
	  tty_fprintf (fp, "\n");
	  tty_fprintf (fp, _("This will be a self-signature.\n"));

	  if ((flags & SIGN_UIDS_LOCAL))
	    {
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp, _("WARNING: the signature will not be marked "
                                 "as non-exportable.\n"));
	    }

	  if ((flags & SIGN_UIDS_NONREVOCABLE))
	    {
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp, _("WARNING: the signature will not be marked "
                                 "as non-revocable.\n"));
	    }
	}
      else
	{
	  if ((flags & SIGN_UIDS_LOCAL))
	    {
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp,
                 _("The signature will be marked as non-exportable.\n"));
	    }

	  if ((flags & SIGN_UIDS_NONREVOCABLE))
	    {
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp,
                 _("The signature will be marked as non-revocable.\n"));
	    }

	  switch (class)
	    {
	    case 0x11:
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp, _("I have not checked this key at all.\n"));
	      break;

	    case 0x12:
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp, _("I have checked this key casually.\n"));
	      break;

	    case 0x13:
	      tty_fprintf (fp, "\n");
	      tty_fprintf (fp, _("I have checked this key very carefully.\n"));
	      break;
	    }
	}

      tty_fprintf (fp, "\n");

      if (opt.batch && opt.answer_yes)
	;
      else if ((flags & SIGN_UIDS_QUICK))
        ;
      else if (!cpr_get_answer_is_yes ("sign_uid.okay",
				       _("Really sign? (y/N) ")))
	continue;

      /* Now we can sign the user ids.  */
    reloop:  /* (Must use this, because we are modifying the list.)  */
      primary_pk = NULL;
      for (node = keyblock; node; node = node->next)
	{
	  if (node->pkt->pkttype == PKT_PUBLIC_KEY)
	    primary_pk = node->pkt->pkt.public_key;
	  else if (node->pkt->pkttype == PKT_USER_ID
		   && (node->flag & NODFLG_MARK_A))
	    {
	      PACKET *pkt;
	      PKT_signature *sig;
	      struct sign_attrib attrib;

	      log_assert (primary_pk);
	      memset (&attrib, 0, sizeof attrib);
	      attrib.non_exportable = !!(flags & SIGN_UIDS_LOCAL);
	      attrib.non_revocable = !!(flags & SIGN_UIDS_NONREVOCABLE);
	      attrib.trust_depth = trust_depth;
	      attrib.trust_value = trust_value;
	      attrib.trust_regexp = trust_regexp;
	      node->flag &= ~NODFLG_MARK_A;

	      /* We force creation of a v4 signature for local
	       * signatures, otherwise we would not generate the
	       * subpacket with v3 keys and the signature becomes
	       * exportable.  */

	      if (selfsig)
		rc = make_keysig_packet (ctrl, &sig, primary_pk,
					 node->pkt->pkt.user_id,
					 NULL,
					 pk,
					 0x13,
                                         0, 0,
					 keygen_add_std_prefs, primary_pk,
                                         NULL);
	      else
		rc = make_keysig_packet (ctrl, &sig, primary_pk,
					 node->pkt->pkt.user_id,
					 NULL,
					 pk,
					 class,
					 timestamp, duration,
					 sign_mk_attrib, &attrib,
                                         NULL);
	      if (rc)
		{
                  write_status_error ("keysig", rc);
		  log_error (_("signing failed: %s\n"), gpg_strerror (rc));
		  goto leave;
		}

	      *ret_modified = 1;	/* We changed the keyblock. */
	      update_trust = 1;

	      pkt = xmalloc_clear (sizeof *pkt);
	      pkt->pkttype = PKT_SIGNATURE;
	      pkt->pkt.signature = sig;
	      insert_kbnode (node, new_kbnode (pkt), PKT_SIGNATURE);
	      goto reloop;
	    }
	}

      /* Delete any sigs that got promoted */
      for (node = keyblock; node; node = node->next)
	if (node->flag & NODFLG_DELSIG)
	  delete_kbnode (node);
    } /* End loop over signators.  */

 leave:
  xfree (trust_regexp);
  trust_regexp = NULL;
  release_sk_list (sk_list);
  return rc;
}


/*
 * Change the passphrase of the primary and all secondary keys.  Note
 * that it is common to use only one passphrase for the primary and
 * all subkeys.  However, this is now (since GnuPG 2.1) all up to the
 * gpg-agent.  Returns 0 on success or an error code.
 */
static gpg_error_t
change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
{
  gpg_error_t err;
  kbnode_t node;
  PKT_public_key *pk;
  int any;
  u32 keyid[2], subid[2];
  char *hexgrip = NULL;
  const char *hexgrip2;
  char *cache_nonce = NULL;
  char *passwd_nonce = NULL;

  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
  if (!node)
    {
      log_error ("Oops; public key missing!\n");
      err = gpg_error (GPG_ERR_INTERNAL);
      goto leave;
    }
  pk = node->pkt->pkt.public_key;
  keyid_from_pk (pk, keyid);

  /* Check whether it is likely that we will be able to change the
     passphrase for any subkey.  */
  for (any = 0, node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY
	  || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	{
          char *serialno;

          pk = node->pkt->pkt.public_key;
          keyid_from_pk (pk, subid);

          xfree (hexgrip);
          err = hexkeygrip_from_pk (pk, &hexgrip);
          if (err)
            goto leave;
          err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL);
          if (!err && serialno)
            ; /* Key on card.  */
          else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
            ; /* Maybe stub key. */
          else if (!err)
            any = 1; /* Key is known.  */
          else
            log_error ("key %s: error getting keyinfo from agent: %s\n",
                       keystr_with_sub (keyid, subid), gpg_strerror (err));
          xfree (serialno);
	}
    }
  err = 0;
  if (!any)
    {
      tty_printf (_("Key has only stub or on-card key items - "
		    "no passphrase to change.\n"));
      goto leave;
    }

  /* Change the passphrase for all keys.  */
  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY
	  || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
          char *desc, *p;

          pk = node->pkt->pkt.public_key;
          keyid_from_pk (pk, subid);

          xfree (hexgrip);
          err = hexkeygrip_from_pk (pk, &hexgrip);
          if (err)
            goto leave;
          if ((p=strchr (hexgrip, ',')))
            {
              *p++ = 0;
              hexgrip2 = p;
            }
          else
            hexgrip2 = NULL;

          /* Note that when using --dry-run we don't change the
           * passphrase but merely verify the current passphrase.  */
          desc = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_NORMAL, 1);
          err = agent_passwd (ctrl, hexgrip, desc, !!opt.dry_run,
                              &cache_nonce, &passwd_nonce);
          if (!err && hexgrip2)
            err = agent_passwd (ctrl, hexgrip2, desc, !!opt.dry_run,
                                &cache_nonce, &passwd_nonce);
          xfree (desc);

          if (err)
            log_log ((gpg_err_code (err) == GPG_ERR_CANCELED
                      || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
                     ? GPGRT_LOGLVL_INFO : GPGRT_LOGLVL_ERROR,
                     _("key %s: error changing passphrase: %s\n"),
                       keystr_with_sub (keyid, subid),
                       gpg_strerror (err));
          if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
            break;
        }
    }

 leave:
  xfree (hexgrip);
  xfree (cache_nonce);
  xfree (passwd_nonce);
  return err;
}



/* 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).  */
static int
fix_keyblock (ctrl_t ctrl, kbnode_t *keyblockp)
{
  int changed = 0;

  if (collapse_uids (keyblockp))
    changed++;
  if (collapse_subkeys (keyblockp))
    changed++;
  if (key_check_all_keysigs (ctrl, 1, *keyblockp, 0, 1))
    changed++;
  reorder_keyblock (*keyblockp);
  /* If we modified the keyblock, make sure the flags are right. */
  if (changed)
    merge_keys_and_selfsig (ctrl, *keyblockp);

  return changed;
}


/* Helper to parse the prefix of the sign command STR and set the
 * respective bits in R_FLAGS.  Returns false on error.  */
static int
parse_sign_type (const char *str, unsigned int *r_flags)
{
  const char *p = str;

  while (*p)
    {
      if (ascii_strncasecmp (p, "l", 1) == 0)
	{
	  *r_flags |= SIGN_UIDS_LOCAL;
	  p++;
	}
      else if (ascii_strncasecmp (p, "nr", 2) == 0)
	{
	  *r_flags |= SIGN_UIDS_NONREVOCABLE;
	  p += 2;
	}
      else if (ascii_strncasecmp (p, "t", 1) == 0)
	{
	  *r_flags |= SIGN_UIDS_TRUSTSIG;
	  p++;
	}
      else
	return 0;
    }

  return 1;
}



/*
 * Menu driven key editor.  If seckey_check is true, then a secret key
 * that matches username will be looked for.  If it is false, not all
 * commands will be available.
 *
 * Note: to keep track of certain selections we use node->mark MARKBIT_xxxx.
 */

/* Need an SK for this command */
#define KEYEDIT_NEED_SK 1
/* Need an SUB KEY for this command */
#define KEYEDIT_NEED_SUBSK 2
/* Match the tail of the string */
#define KEYEDIT_TAIL_MATCH 8

enum cmdids
{
  cmdNONE = 0,
  cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
  cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG,
  cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY,
  cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
  cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, cmdADDADSK,
#ifndef NO_TRUST_MODELS
  cmdENABLEKEY, cmdDISABLEKEY,
#endif /*!NO_TRUST_MODELS*/
  cmdSHOWPREF,
  cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
  cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdKEYTOTPM, cmdBKUPTOCARD,
  cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP
};

static struct
{
  const char *name;
  enum cmdids id;
  int flags;
  const char *desc;
} cmds[] =
{
  { "quit", cmdQUIT, 0, N_("quit this menu")},
  { "q", cmdQUIT, 0, NULL},
  { "save", cmdSAVE, 0, N_("save and quit")},
  { "help", cmdHELP, 0, N_("show this help")},
  { "?", cmdHELP, 0, NULL},
  { "fpr", cmdFPR, 0, N_("show key fingerprint")},
  { "grip", cmdGRIP, 0, N_("show the keygrip")},
  { "list", cmdLIST, 0, N_("list key and user IDs")},
  { "l", cmdLIST, 0, NULL},
  { "uid", cmdSELUID, 0, N_("select user ID N")},
  { "key", cmdSELKEY, 0, N_("select subkey N")},
  { "check", cmdCHECK, 0, N_("check signatures")},
  { "c", cmdCHECK, 0, NULL},
  { "change-usage", cmdCHANGEUSAGE, KEYEDIT_NEED_SK, NULL},
  { "cross-certify", cmdBACKSIGN, KEYEDIT_NEED_SK, NULL},
  { "backsign", cmdBACKSIGN,  KEYEDIT_NEED_SK, NULL},
  { "sign", cmdSIGN,  KEYEDIT_TAIL_MATCH,
    N_("sign selected user IDs [* see below for related commands]")},
  { "s", cmdSIGN, 0, NULL},
    /* "lsign" and friends will never match since "sign" comes first
       and it is a tail match.  They are just here so they show up in
       the help menu. */
  { "lsign", cmdNOP, 0, N_("sign selected user IDs locally")},
  { "tsign", cmdNOP, 0, N_("sign selected user IDs with a trust signature")},
  { "nrsign", cmdNOP, 0,
    N_("sign selected user IDs with a non-revocable signature")},
  { "debug", cmdDEBUG, 0, NULL},
  { "adduid", cmdADDUID,  KEYEDIT_NEED_SK, N_("add a user ID")},
  { "addphoto", cmdADDPHOTO,  KEYEDIT_NEED_SK,
    N_("add a photo ID")},
  { "deluid", cmdDELUID, 0, N_("delete selected user IDs")},
    /* delphoto is really deluid in disguise */
  { "delphoto", cmdDELUID, 0, NULL},
  { "addkey", cmdADDKEY,  KEYEDIT_NEED_SK, N_("add a subkey")},
#ifdef ENABLE_CARD_SUPPORT
  { "addcardkey", cmdADDCARDKEY,  KEYEDIT_NEED_SK,
    N_("add a key to a smartcard")},
  { "keytocard", cmdKEYTOCARD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
    N_("move a key to a smartcard")},
  { "keytotpm", cmdKEYTOTPM, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
    N_("convert a key to TPM form using the local TPM")},
  { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
    N_("move a backup key to a smartcard")},
#endif /*ENABLE_CARD_SUPPORT */
  { "delkey", cmdDELKEY, 0, N_("delete selected subkeys")},
  { "addrevoker", cmdADDREVOKER,  KEYEDIT_NEED_SK,
    N_("add a revocation key")},
  { "addadsk", cmdADDADSK,  KEYEDIT_NEED_SK,
    N_("add an additional decryption subkey")},
  { "delsig", cmdDELSIG, 0,
    N_("delete signatures from the selected user IDs")},
  { "expire", cmdEXPIRE,  KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
    N_("change the expiration date for the key or selected subkeys")},
  { "primary", cmdPRIMARY,  KEYEDIT_NEED_SK,
    N_("flag the selected user ID as primary")},
  { "toggle", cmdTOGGLE, KEYEDIT_NEED_SK, NULL},  /* Dummy command.  */
  { "t", cmdTOGGLE, KEYEDIT_NEED_SK, NULL},
  { "pref", cmdPREF, 0, N_("list preferences (expert)")},
  { "showpref", cmdSHOWPREF, 0, N_("list preferences (verbose)")},
  { "setpref", cmdSETPREF,  KEYEDIT_NEED_SK,
    N_("set preference list for the selected user IDs")},
  { "updpref", cmdSETPREF,  KEYEDIT_NEED_SK, NULL},
  { "keyserver", cmdPREFKS,  KEYEDIT_NEED_SK,
    N_("set the preferred keyserver URL for the selected user IDs")},
  { "notation", cmdNOTATION,  KEYEDIT_NEED_SK,
    N_("set a notation for the selected user IDs")},
  { "passwd", cmdPASSWD,  KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
    N_("change the passphrase")},
  { "password", cmdPASSWD,  KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, NULL},
#ifndef NO_TRUST_MODELS
  { "trust", cmdTRUST, 0, N_("change the ownertrust")},
#endif /*!NO_TRUST_MODELS*/
  { "revsig", cmdREVSIG, 0,
    N_("revoke signatures on the selected user IDs")},
  { "revuid", cmdREVUID,  KEYEDIT_NEED_SK,
    N_("revoke selected user IDs")},
  { "revphoto", cmdREVUID,  KEYEDIT_NEED_SK, NULL},
  { "revkey", cmdREVKEY,  KEYEDIT_NEED_SK,
    N_("revoke key or selected subkeys")},
#ifndef NO_TRUST_MODELS
  { "enable", cmdENABLEKEY, 0, N_("enable key")},
  { "disable", cmdDISABLEKEY, 0, N_("disable key")},
#endif /*!NO_TRUST_MODELS*/
  { "showphoto", cmdSHOWPHOTO, 0, N_("show selected photo IDs")},
  { "clean", cmdCLEAN, 0,
    N_("compact unusable user IDs and remove unusable signatures from key")},
  { "minimize", cmdMINIMIZE, 0,
    N_("compact unusable user IDs and remove all signatures from key")},

  { NULL, cmdNONE, 0, NULL}
};



#ifdef HAVE_LIBREADLINE

/*
   These two functions are used by readline for command completion.
 */

static char *
command_generator (const char *text, int state)
{
  static int list_index, len;
  const char *name;

  /* If this is a new word to complete, initialize now.  This includes
     saving the length of TEXT for efficiency, and initializing the
     index variable to 0. */
  if (!state)
    {
      list_index = 0;
      len = strlen (text);
    }

  /* Return the next partial match */
  while ((name = cmds[list_index].name))
    {
      /* Only complete commands that have help text */
      if (cmds[list_index++].desc && strncmp (name, text, len) == 0)
	return strdup (name);
    }

  return NULL;
}

static char **
keyedit_completion (const char *text, int start, int end)
{
  /* If we are at the start of a line, we try and command-complete.
     If not, just do nothing for now. */

  (void) end;

  if (start == 0)
    return rl_completion_matches (text, command_generator);

  rl_attempted_completion_over = 1;

  return NULL;
}
#endif /* HAVE_LIBREADLINE */



/* Main function of the menu driven key editor.  */
void
keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
	      strlist_t commands, int quiet, int seckey_check)
{
  enum cmdids cmd = 0;
  gpg_error_t err = 0;
  KBNODE keyblock = NULL;
  KEYDB_HANDLE kdbhd = NULL;
  int have_seckey = 0;
  int have_anyseckey = 0;
  char *answer = NULL;
  int redisplay = 1;
  int modified = 0;
  int upload = 0;  /* Set if the key maybe be uploaded.  */
  int sec_shadowing = 0;
  int run_subkey_warnings = 0;
  int have_commands = !!commands;
  strlist_t delseckey_list = NULL;
  int delseckey_list_warn = 0;

  if (opt.command_fd != -1)
    ;
  else if (opt.batch && !have_commands)
    {
      log_error (_("can't do this in batch mode\n"));
      goto leave;
    }

#ifdef HAVE_W32_SYSTEM
  /* Due to Windows peculiarities we need to make sure that the
     trustdb stale check is done before we open another file
     (i.e. by searching for a key).  In theory we could make sure
     that the files are closed after use but the open/close caches
     inhibits that and flushing the cache right before the stale
     check is not easy to implement.  Thus we take the easy way out
     and run the stale check as early as possible.  Note, that for
     non- W32 platforms it is run indirectly through a call to
     get_validity ().  */
  check_trustdb_stale (ctrl);
#endif

  /* Get the public key */
  err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
                           NULL, NULL, username, &keyblock, &kdbhd, 1);
  if (err)
    {
      log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err));
      goto leave;
    }

  if (fix_keyblock (ctrl, &keyblock))
    modified++;

  /* See whether we have a matching secret key.  */
  if (seckey_check)
    {
      have_anyseckey = !agent_probe_any_secret_key (ctrl, keyblock);
      if (have_anyseckey
          && agent_probe_secret_key (ctrl, keyblock->pkt->pkt.public_key))
        {
          /* The primary key is also available.   */
          have_seckey = 1;
        }

      if (have_seckey && !quiet)
        tty_printf (_("Secret key is available.\n"));
      else if (have_anyseckey && !quiet)
        tty_printf (_("Secret subkeys are available.\n"));
    }

  /* Main command loop.  */
  for (;;)
    {
      int i, arg_number, photo;
      const char *arg_string = "";
      char *p;
      PKT_public_key *pk = keyblock->pkt->pkt.public_key;

      tty_printf ("\n");

      if (redisplay && !quiet)
	{
          /* Show using flags: with_revoker, with_subkeys.  */
	  show_key_with_all_names (ctrl, NULL, keyblock, 0, 1, 0, 1, 0, 0);
	  tty_printf ("\n");
	  redisplay = 0;
	}

      if (run_subkey_warnings)
        {
          run_subkey_warnings = 0;
          if (!count_selected_keys (keyblock))
            subkey_expire_warning (keyblock);
          no_usable_encr_subkeys_warning (keyblock);
        }

      if (delseckey_list_warn)
        {
          delseckey_list_warn = 0;
          tty_printf
            (_("Note: the local copy of the secret key"
               " will only be deleted with \"save\".\n"));
        }

      do
	{
	  xfree (answer);
	  if (have_commands)
	    {
	      if (commands)
		{
		  answer = xstrdup (commands->d);
		  commands = commands->next;
		}
	      else if (opt.batch)
		{
		  answer = xstrdup ("quit");
		}
	      else
		have_commands = 0;
	    }
	  if (!have_commands)
	    {
#ifdef HAVE_LIBREADLINE
	      tty_enable_completion (keyedit_completion);
#endif
	      answer = cpr_get_no_help ("keyedit.prompt", GPG_NAME "> ");
	      cpr_kill_prompt ();
	      tty_disable_completion ();
	    }
	  trim_spaces (answer);
	}
      while (*answer == '#');

      arg_number = 0; /* Here is the init which egcc complains about.  */
      photo = 0;      /* Same here. */
      if (!*answer)
	cmd = cmdLIST;
      else if (*answer == CONTROL_D)
	cmd = cmdQUIT;
      else if (digitp (answer))
	{
	  cmd = cmdSELUID;
	  arg_number = atoi (answer);
	}
      else
	{
	  if ((p = strchr (answer, ' ')))
	    {
	      *p++ = 0;
	      trim_spaces (answer);
	      trim_spaces (p);
	      arg_number = atoi (p);
	      arg_string = p;
	    }

	  for (i = 0; cmds[i].name; i++)
	    {
	      if (cmds[i].flags & KEYEDIT_TAIL_MATCH)
		{
		  size_t l = strlen (cmds[i].name);
		  size_t a = strlen (answer);
		  if (a >= l)
		    {
		      if (!ascii_strcasecmp (&answer[a - l], cmds[i].name))
			{
			  answer[a - l] = '\0';
			  break;
			}
		    }
		}
	      else if (!ascii_strcasecmp (answer, cmds[i].name))
		break;
	    }
	  if ((cmds[i].flags & (KEYEDIT_NEED_SK|KEYEDIT_NEED_SUBSK))
              && !(((cmds[i].flags & KEYEDIT_NEED_SK) && have_seckey)
                   || ((cmds[i].flags & KEYEDIT_NEED_SUBSK) && have_anyseckey)))
	    {
	      tty_printf (_("Need the secret key to do this.\n"));
	      cmd = cmdNOP;
	    }
          else
	    cmd = cmds[i].id;
	}

      /* Dispatch the command.  */
      switch (cmd)
	{
	case cmdHELP:
	  for (i = 0; cmds[i].name; i++)
	    {
              if ((cmds[i].flags & (KEYEDIT_NEED_SK|KEYEDIT_NEED_SUBSK))
                  && !(((cmds[i].flags & KEYEDIT_NEED_SK) && have_seckey)
                       ||((cmds[i].flags&KEYEDIT_NEED_SUBSK)&&have_anyseckey)))
		; /* Skip those item if we do not have the secret key.  */
	      else if (cmds[i].desc)
		tty_printf ("%-11s %s\n", cmds[i].name, _(cmds[i].desc));
	    }

	  tty_printf ("\n");
	  tty_printf
            (_("* The 'sign' command may be prefixed with an 'l' for local "
               "signatures (lsign),\n"
               "  a 't' for trust signatures (tsign), an 'nr' for "
               "non-revocable signatures\n"
               "  (nrsign), or any combination thereof (ltsign, "
               "tnrsign, etc.).\n"));
	  break;

	case cmdLIST:
	  redisplay = 1;
	  break;

	case cmdFPR:
	  show_key_and_fingerprint
            (ctrl,
             keyblock, (*arg_string == '*'
                        && (!arg_string[1] || spacep (arg_string + 1))));
	  break;

	case cmdGRIP:
	  show_key_and_grip (keyblock);
	  break;

	case cmdSELUID:
	  if (strlen (arg_string) == NAMEHASH_LEN * 2)
	    redisplay = menu_select_uid_namehash (keyblock, arg_string);
	  else
	    {
	      if (*arg_string == '*'
		  && (!arg_string[1] || spacep (arg_string + 1)))
		arg_number = -1;	/* Select all. */
	      redisplay = menu_select_uid (keyblock, arg_number);
	    }
	  break;

	case cmdSELKEY:
	  {
	    if (*arg_string == '*'
		&& (!arg_string[1] || spacep (arg_string + 1)))
	      arg_number = -1;	/* Select all. */
	    if (menu_select_key (keyblock, arg_number, p))
	      redisplay = 1;
	  }
	  break;

	case cmdCHECK:
	  if (key_check_all_keysigs (ctrl, -1, keyblock,
				     count_selected_uids (keyblock),
				     !strcmp (arg_string, "selfsig")))
            modified = 1;
	  break;

	case cmdSIGN:
	  {
            unsigned int myflags = 0;
            int my_modified = 0;

	    if (pk->flags.revoked)
	      {
		tty_printf (_("Key is revoked."));

		if (opt.expert)
		  {
		    tty_printf ("  ");
		    if (!cpr_get_answer_is_yes
                        ("keyedit.sign_revoked.okay",
                         _("Are you sure you still want to sign it? (y/N) ")))
		      break;
		  }
		else
		  {
		    tty_printf (_("  Unable to sign.\n"));
		    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 text user IDs? (y/N) "));
                else
                  result = cpr_get_answer_is_yes
                    ("keyedit.sign_all.okay",
                     _("Really sign all user IDs? (y/N) "));

                if (! result)
                  {
                    if (opt.interactive)
                      myflags |= SIGN_UIDS_INTERACTIVE;
                    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, &myflags))
	      {
		tty_printf (_("Unknown signature type '%s'\n"), answer);
		break;
	      }

	    sign_uids (ctrl, NULL, keyblock, locusr, myflags,
                       NULL, &my_modified);
            if (my_modified)  /* sign_uids modified the keyblock      */
              modified = 1;   /* thus set the general modified flag.  */
            if (my_modified && !(myflags & SIGN_UIDS_LOCAL))
              upload = 1;     /* exportable signature -> mark uploadable.  */
	  }
	  break;

	case cmdDEBUG:
	  dump_kbnode (keyblock);
	  break;

	case cmdTOGGLE:
          /* The toggle command is a leftover from old gpg versions
             where we worked with a secret and a public keyring.  It
             is not necessary anymore but we keep this command for the
             sake of scripts using it.  */
	  redisplay = 1;
	  break;

	case cmdADDPHOTO:
	  if (RFC2440)
	    {
	      tty_printf (_("This command is not allowed while in %s mode.\n"),
			  gnupg_compliance_option_string (opt.compliance));
	      break;
	    }
	  photo = 1;
	  /* fall through */
	case cmdADDUID:
	  if (menu_adduid (ctrl, keyblock, photo, arg_string, NULL))
	    {
	      update_trust = 1;
	      redisplay = 1;
	      modified = 1;
              upload = 1;
	      merge_keys_and_selfsig (ctrl, keyblock);
	    }
	  break;

	case cmdDELUID:
	  {
	    int n1;

	    if (!(n1 = count_selected_uids (keyblock)))
              {
                tty_printf (_("You must select at least one user ID.\n"));
                if (!opt.expert)
                  tty_printf (_("(Use the '%s' command.)\n"), "uid");
              }
	    else if (real_uids_left (keyblock) < 1)
	      tty_printf (_("You can't delete the last user ID!\n"));
	    else if (cpr_get_answer_is_yes
                     ("keyedit.remove.uid.okay",
                      n1 > 1 ? _("Really remove all selected user IDs? (y/N) ")
                      :        _("Really remove this user ID? (y/N) ")))
	      {
		menu_deluid (keyblock);
		redisplay = 1;
		modified = 1;
                /* upload does not make sense here.  Eventually we may
                 * decide to delete a key from the keyserver.*/
	      }
	  }
	  break;

	case cmdDELSIG:
	  {
	    int n1;

	    if (!(n1 = count_selected_uids (keyblock)))
              {
                tty_printf (_("You must select at least one user ID.\n"));
                if (!opt.expert)
                  tty_printf (_("(Use the '%s' command.)\n"), "uid");
              }
	    else if (menu_delsig (ctrl, keyblock))
	      {
		/* No redisplay here, because it may scroll away some
		 * of the status output of this command.  */
		modified = 1;
	      }
	  }
	  break;

	case cmdADDKEY:
	  if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL))
	    {
	      redisplay = 1;
	      modified = 1;
              upload = 1;
	      merge_keys_and_selfsig (ctrl, keyblock);
	    }
	  break;

#ifdef ENABLE_CARD_SUPPORT
	case cmdADDCARDKEY:
	  if (!card_generate_subkey (ctrl, keyblock))
	    {
	      redisplay = 1;
	      modified = 1;
              upload = 1;
	      merge_keys_and_selfsig (ctrl, keyblock);
	    }
	  break;

	case cmdKEYTOTPM:
	  /* FIXME need to store the key and not commit until later */
	  {
	    kbnode_t node = NULL;
	    switch (count_selected_keys (keyblock))
	      {
	      case 0:
		if (cpr_get_answer_is_yes
                    ("keyedit.keytocard.use_primary",
                     /* TRANSLATORS: Please take care: This is about
                        moving the key and not about removing it.  */
                     _("Really move the primary key? (y/N) ")))
		  node = keyblock;
		break;
	      case 1:
		for (node = keyblock; node; node = node->next)
		  {
		    if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
			&& node->flag & NODFLG_SELKEY)
		      break;
		  }
		break;
	      default:
		tty_printf (_("You must select exactly one key.\n"));
		break;
	      }
	    if (node)
	      {
		PKT_public_key *xxpk = node->pkt->pkt.public_key;
		char *hexgrip;

		hexkeygrip_from_pk (xxpk, &hexgrip);
		if (!agent_keytotpm (ctrl, hexgrip))
		  {
		    redisplay = 1;
		  }
		xfree (hexgrip);
	      }
	  }
	  break;

	case cmdKEYTOCARD:
	  {
	    KBNODE node = NULL;
	    switch (count_selected_keys (keyblock))
	      {
	      case 0:
		if (cpr_get_answer_is_yes
                    ("keyedit.keytocard.use_primary",
                     /* TRANSLATORS: Please take care: This is about
                        moving the key and not about removing it.  */
                     _("Really move the primary key? (y/N) ")))
		  node = keyblock;
		break;
	      case 1:
		for (node = keyblock; node; node = node->next)
		  {
		    if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
			&& node->flag & NODFLG_SELKEY)
		      break;
		  }
		break;
	      default:
		tty_printf (_("You must select exactly one key.\n"));
		break;
	      }
	    if (node)
	      {
		PKT_public_key *xxpk = node->pkt->pkt.public_key;
		if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0,
                                       &delseckey_list))
		  {
		    redisplay = 1;
		    sec_shadowing = 1;
                    delseckey_list_warn = 1;
		  }
	      }
	  }
	  break;

	case cmdBKUPTOCARD:
	  {
	    /* 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;
	    char *fname;
	    PACKET *pkt;
	    IOBUF a;
            struct parse_packet_ctx_s parsectx;
            int lastmode;

            if (!*arg_string)
	      {
		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)))
	      {
		iobuf_close (a);
		a = NULL;
		gpg_err_set_errno (EPERM);
	      }
            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);
	    init_packet (pkt);
            init_parse_packet (&parsectx, a);
	    err = parse_packet (&parsectx, pkt);
	    deinit_parse_packet (&parsectx);
            iobuf_close (a);
	    iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char *) fname);
	    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));
                xfree (fname);
                free_packet (pkt, NULL);
                xfree (pkt);
                break;
              }

	    xfree (fname);
	    node = new_kbnode (pkt);

            err = agent_set_ephemeral_mode (ctrl, 1, &lastmode);
            if (err)
              log_error ("error switching to ephemeral mode: %s\n",
                         gpg_strerror (err));
            else
              {
                /* Transfer it to gpg-agent which handles secret keys.  */
                err = transfer_secret_keys (ctrl, NULL, node, 1, 1, 0);
                if (!err)
                  {
                    /* Treat the pkt as a public key.  */
                    pkt->pkttype = PKT_PUBLIC_KEY;

                    /* Ask gpg-agent to store the secret key to card.  */
                    if (card_store_subkey (node, 0, NULL))
                      {
                        redisplay = 1;
                        sec_shadowing = 1;
                      }
                  }
                if (!lastmode && agent_set_ephemeral_mode (ctrl, 0, NULL))
                  log_error ("error clearing the ephemeral mode\n");
              }
            release_kbnode (node);
          }
          break;

#endif /* ENABLE_CARD_SUPPORT */

	case cmdDELKEY:
	  {
	    int n1;

	    if (!(n1 = count_selected_keys (keyblock)))
              {
                tty_printf (_("You must select at least one key.\n"));
                if (!opt.expert)
                  tty_printf (_("(Use the '%s' command.)\n"), "key");
              }
	    else if (!cpr_get_answer_is_yes
                     ("keyedit.remove.subkey.okay",
                      n1 > 1 ? _("Do you really want to delete the "
                                 "selected keys? (y/N) ")
                      :  _("Do you really want to delete this key? (y/N) ")))
	      ;
	    else
	      {
		menu_delkey (keyblock);
		redisplay = 1;
		modified = 1;
                /* upload does not make sense. */
	      }
	  }
	  break;

	case cmdADDREVOKER:
	  {
	    int sensitive = 0;

	    if (ascii_strcasecmp (arg_string, "sensitive") == 0)
	      sensitive = 1;
	    if (menu_addrevoker (ctrl, keyblock, sensitive))
	      {
		redisplay = 1;
		modified = 1;
                upload = 1;
		merge_keys_and_selfsig (ctrl, keyblock);
	      }
	  }
	  break;

	case cmdADDADSK:
          if (menu_addadsk (ctrl, keyblock, NULL))
            {
              redisplay = 1;
              modified = 1;
              upload = 1;
              merge_keys_and_selfsig (ctrl, keyblock);
            }
	  break;

	case cmdREVUID:
	  {
	    int n1;

	    if (!(n1 = count_selected_uids (keyblock)))
              {
                tty_printf (_("You must select at least one user ID.\n"));
                if (!opt.expert)
                  tty_printf (_("(Use the '%s' command.)\n"), "uid");
              }
	    else if (cpr_get_answer_is_yes
                     ("keyedit.revoke.uid.okay",
                      n1 > 1 ? _("Really revoke all selected user IDs? (y/N) ")
		      :        _("Really revoke this user ID? (y/N) ")))
	      {
		if (menu_revuid (ctrl, keyblock))
		  {
		    modified = 1;
		    redisplay = 1;
                    upload = 1;
		  }
	      }
	  }
	  break;

	case cmdREVKEY:
	  {
	    int n1;

	    if (!(n1 = count_selected_keys (keyblock)))
	      {
		if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay",
					   _("Do you really want to revoke"
					     " the entire key? (y/N) ")))
		  {
		    if (menu_revkey (ctrl, keyblock))
                      {
                        modified = 1;
                        upload = 1;
                      }

		    redisplay = 1;
		  }
	      }
	    else if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay",
					    n1 > 1 ?
					    _("Do you really want to revoke"
					      " the selected subkeys? (y/N) ")
					    : _("Do you really want to revoke"
						" this subkey? (y/N) ")))
	      {
		if (menu_revsubkey (ctrl, keyblock))
                  {
                    modified = 1;
                    upload = 1;
                  }

		redisplay = 1;
	      }

	    if (modified)
	      merge_keys_and_selfsig (ctrl, keyblock);
	  }
	  break;

	case cmdEXPIRE:
	  if (gpg_err_code (menu_expire (ctrl, keyblock, 0, 0)) == GPG_ERR_TRUE)
	    {
	      merge_keys_and_selfsig (ctrl, keyblock);
              run_subkey_warnings = 1;
	      modified = 1;
              upload = 1;
	      redisplay = 1;
	    }
	  break;

	case cmdCHANGEUSAGE:
	  if (menu_changeusage (ctrl, keyblock))
	    {
	      merge_keys_and_selfsig (ctrl, keyblock);
	      modified = 1;
              upload = 1;
	      redisplay = 1;
	    }
	  break;

	case cmdBACKSIGN:
	  if (menu_backsign (ctrl, keyblock))
	    {
	      modified = 1;
              upload = 1;
	      redisplay = 1;
	    }
	  break;

	case cmdPRIMARY:
	  if (menu_set_primary_uid (ctrl, keyblock))
	    {
	      merge_keys_and_selfsig (ctrl, keyblock);
	      modified = 1;
              upload = 1;
	      redisplay = 1;
	    }
	  break;

	case cmdPASSWD:
	  change_passphrase (ctrl, keyblock);
	  break;

#ifndef NO_TRUST_MODELS
	case cmdTRUST:
	  if (opt.trust_model == TM_EXTERNAL)
	    {
	      tty_printf (_("Owner trust may not be set while "
			    "using a user provided trust database\n"));
	      break;
	    }

	  show_key_with_all_names (ctrl, NULL, keyblock, 0, 0, 0, 1, 0, 0);
	  tty_printf ("\n");
	  if (edit_ownertrust (ctrl, find_kbnode (keyblock,
					    PKT_PUBLIC_KEY)->pkt->pkt.
			       public_key, 1))
	    {
	      redisplay = 1;
	      /* No real need to set update_trust here as
	         edit_ownertrust() calls revalidation_mark()
	         anyway. */
	      update_trust = 1;
	    }
	  break;
#endif /*!NO_TRUST_MODELS*/

	case cmdPREF:
	  {
	    int count = count_selected_uids (keyblock);
	    log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
	    show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key,
			count ? NODFLG_SELUID : 0, 1);
	  }
	  break;

	case cmdSHOWPREF:
	  {
	    int count = count_selected_uids (keyblock);
	    log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
	    show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key,
			count ? NODFLG_SELUID : 0, 2);
	  }
	  break;

	case cmdSETPREF:
	  {
	    PKT_user_id *tempuid;

	    keygen_set_std_prefs (!*arg_string ? "default" : arg_string, 0);

	    tempuid = keygen_get_std_prefs ();
	    tty_printf (_("Set preference list to:\n"));
	    show_prefs (tempuid, NULL, 1);
	    free_user_id (tempuid);

	    if (cpr_get_answer_is_yes
                ("keyedit.setpref.okay",
                 count_selected_uids (keyblock) ?
                 _("Really update the preferences"
                   " for the selected user IDs? (y/N) ")
                 : _("Really update the preferences? (y/N) ")))
	      {
		if (menu_set_preferences (ctrl, keyblock, 0))
		  {
		    merge_keys_and_selfsig (ctrl, keyblock);
		    modified = 1;
                    upload = 1;
		    redisplay = 1;
		  }
	      }
	  }
	  break;

	case cmdPREFKS:
	  if (menu_set_keyserver_url (ctrl, *arg_string ? arg_string : NULL,
				      keyblock))
	    {
	      merge_keys_and_selfsig (ctrl, keyblock);
	      modified = 1;
              upload = 1;
	      redisplay = 1;
	    }
	  break;

	case cmdNOTATION:
	  if (menu_set_notation (ctrl, *arg_string ? arg_string : NULL,
				 keyblock))
	    {
	      merge_keys_and_selfsig (ctrl, keyblock);
	      modified = 1;
              upload = 1;
	      redisplay = 1;
	    }
	  break;

	case cmdNOP:
	  break;

	case cmdREVSIG:
	  if (menu_revsig (ctrl, keyblock))
	    {
	      redisplay = 1;
	      modified = 1;
              upload = 1;
	    }
	  break;

#ifndef NO_TRUST_MODELS
	case cmdENABLEKEY:
	case cmdDISABLEKEY:
	  if (enable_disable_key (ctrl, keyblock, cmd == cmdDISABLEKEY))
	    {
	      redisplay = 1;
	      modified = 1;
	    }
	  break;
#endif /*!NO_TRUST_MODELS*/

	case cmdSHOWPHOTO:
	  menu_showphoto (ctrl, keyblock);
	  break;

	case cmdCLEAN:
	  if (menu_clean (ctrl, keyblock, 0))
	    redisplay = modified = 1;
	  break;

	case cmdMINIMIZE:
	  if (menu_clean (ctrl, keyblock, EXPORT_MINIMAL))
	    redisplay = modified = 1;
	  break;

	case cmdQUIT:
	  if (have_commands)
	    goto leave;
	  if (!modified && !sec_shadowing)
	    goto leave;
	  if (!cpr_get_answer_is_yes ("keyedit.save.okay",
				      _("Save changes? (y/N) ")))
	    {
	      if (cpr_enabled ()
		  || cpr_get_answer_is_yes ("keyedit.cancel.okay",
					    _("Quit without saving? (y/N) ")))
		goto leave;
	      break;
	    }
	  /* fall through */
	case cmdSAVE:
	  if (modified)
	    {
              err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
              if (err)
                {
                  log_error (_("update failed: %s\n"), gpg_strerror (err));
                  break;
                }
              if (upload)
                {
                  maybe_upload_key (ctrl, keyblock);
                  upload = 0;
                }
	    }

          if (delseckey_list)
            {
              strlist_t sl;
              for (err = 0, sl = delseckey_list; sl; sl = sl->next)
                {
                  if (*sl->d)
                    {
                      err = agent_delete_key (ctrl, sl->d, NULL, 1/*force*/);
                      if (err)
                        break;
                      *sl->d = 0;  /* Mark deleted.  */
                    }
                }
              if (err)
                {
                  log_error (_("deleting copy of secret key failed: %s\n"),
                             gpg_strerror (err));
                  break; /* the "save".  */
                }
            }

	  if (sec_shadowing)
	    {
	      err = agent_scd_learn (NULL, 1);
	      if (err)
                {
                  log_error (_("update failed: %s\n"), gpg_strerror (err));
                  break;
                }
	    }

	  if (!modified && !sec_shadowing)
	    tty_printf (_("Key not changed so no update needed.\n"));

	  if (update_trust)
	    {
	      revalidation_mark (ctrl);
	      update_trust = 0;
	    }
	  goto leave;

	case cmdINVCMD:
	default:
	  tty_printf ("\n");
	  tty_printf (_("Invalid command  (try \"help\")\n"));
	  break;
	}
    } /* End of the main command loop.  */

 leave:
  free_strlist (delseckey_list);
  release_kbnode (keyblock);
  keydb_release (kdbhd);
  xfree (answer);
}


/* Helper to upload a key to an LDAP server if configured.  */
static void
maybe_upload_key (ctrl_t ctrl, kbnode_t keyblock)
{
  unsigned int saved_options;

  if (!opt.flags.auto_key_upload)
    return;

  saved_options = opt.keyserver_options.options;
  opt.keyserver_options.options |= KEYSERVER_LDAP_ONLY;
  opt.keyserver_options.options |= KEYSERVER_WARN_ONLY;
  keyserver_export_pubkey (ctrl, keyblock->pkt->pkt.public_key, 0);
  opt.keyserver_options.options = saved_options;
}


/* Change the passphrase of the secret key identified by USERNAME.  */
void
keyedit_passwd (ctrl_t ctrl, const char *username)
{
  gpg_error_t err;
  PKT_public_key *pk;
  kbnode_t keyblock = NULL;

  pk = xtrycalloc (1, sizeof *pk);
  if (!pk)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  err = getkey_byname (ctrl, NULL, pk, username, 1, &keyblock);
  if (err)
    goto leave;

  err = change_passphrase (ctrl, keyblock);

leave:
  release_kbnode (keyblock);
  free_public_key (pk);
  if (err)
    {
      log_info ("error changing the passphrase for '%s': %s\n",
		username, gpg_strerror (err));
      write_status_error ("keyedit.passwd", err);
    }
  else
    write_status_text (STATUS_SUCCESS, "keyedit.passwd");
}


/* 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, int want_secret,
                     KEYDB_HANDLE *r_kdbhd, kbnode_t *r_keyblock)
{
  gpg_error_t err;
  KEYDB_HANDLE kdbhd = NULL;
  kbnode_t keyblock = NULL;
  KEYDB_SEARCH_DESC desc;
  kbnode_t node;

  *r_kdbhd = NULL;
  *r_keyblock = NULL;

  /* Search the key; we don't want the whole getkey stuff here.  */
  kdbhd = keydb_new (ctrl);
  if (!kdbhd)
    {
      /* Note that keydb_new has already used log_error.  */
      err = gpg_error_from_syserror ();
      goto leave;
    }
  err = keydb_lock (kdbhd);
  if (err)
    goto leave;

  err = classify_user_id (username, &desc, 1);
  if (!err)
    err = keydb_search (kdbhd, &desc, 1, NULL);
  if (!err)
    {
      err = keydb_get_keyblock (kdbhd, &keyblock);
      if (err)
        {
          log_error (_("error reading keyblock: %s\n"), gpg_strerror (err));
          goto leave;
        }
      /* Now with the keyblock retrieved, search again to detect an
         ambiguous specification.  We need to save the found state so
         that we can do an update later.  */
      keydb_push_found_state (kdbhd);
      err = keydb_search (kdbhd, &desc, 1, NULL);
      if (!err)
        err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
      else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
        err = 0;
      keydb_pop_found_state (kdbhd);

      if (!err && want_secret)
        {
          /* We require the secret primary key to set the primary UID.  */
          node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
          log_assert (node);
          if (!agent_probe_secret_key (ctrl, node->pkt->pkt.public_key))
            err = gpg_error (GPG_ERR_NO_SECKEY);
        }
    }
  else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
    err = gpg_error (GPG_ERR_NO_PUBKEY);

  if (err)
    {
      log_error (_("key \"%s\" not found: %s\n"),
                 username, gpg_strerror (err));
      goto leave;
    }

  fix_keyblock (ctrl, &keyblock);
  merge_keys_and_selfsig (ctrl, keyblock);

  *r_keyblock = keyblock;
  keyblock = NULL;
  *r_kdbhd = kdbhd;
  kdbhd = NULL;

 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, 1, &kdbhd, &keyblock);
  if (err)
    goto leave;

  if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring))
    {
      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
      if (err)
        {
          log_error (_("update failed: %s\n"), gpg_strerror (err));
          goto leave;
        }
      maybe_upload_key (ctrl, keyblock);
      if (update_trust)
        revalidation_mark (ctrl);
    }

 leave:
  xfree (uidstring);
  release_kbnode (keyblock);
  keydb_release (kdbhd);
}


/* Helper to find the UID node for namehash.  On success, returns the UID node.
   Otherwise, return NULL. */
kbnode_t
find_userid_by_namehash (kbnode_t keyblock, const char *namehash, int want_valid)
{
  byte hash[NAMEHASH_LEN];
  kbnode_t node = NULL;

  if (!namehash)
    goto leave;

  if (strlen (namehash) != NAMEHASH_LEN * 2)
    goto leave;

  if (hex2bin (namehash, hash, NAMEHASH_LEN) < 0)
    goto leave;

  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID
          && (!want_valid || (!node->pkt->pkt.user_id->flags.revoked
                              && !node->pkt->pkt.user_id->flags.expired)))
	{
	  namehash_from_uid (node->pkt->pkt.user_id);
	  if (!memcmp (node->pkt->pkt.user_id->namehash, hash, NAMEHASH_LEN))
	    break;
        }
    }

 leave:
  return node;
}


/* Helper to find the UID node for uid.  On success, returns the UID node.
   Otherwise, return NULL. */
kbnode_t
find_userid (kbnode_t keyblock, const char *uid, int want_valid)
{
  kbnode_t node = NULL;
  size_t uidlen;

  if (!keyblock || !uid)
    goto leave;

  /* First try to find UID by namehash. */
  node = find_userid_by_namehash (keyblock, uid, want_valid);
  if (node)
    goto leave;

  uidlen = strlen (uid);
  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID
          && (!want_valid || (!node->pkt->pkt.user_id->flags.revoked
                              && !node->pkt->pkt.user_id->flags.expired))
          && uidlen == node->pkt->pkt.user_id->len
          && !memcmp (node->pkt->pkt.user_id->name, uid, uidlen))
        break;
    }

 leave:
  return node;
}


/* Unattended revocation of a keyid.  USERNAME specifies the
   key. UIDTOREV is the user id revoke from the key.  */
void
keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
{
  gpg_error_t err;
  KEYDB_HANDLE kdbhd = NULL;
  kbnode_t keyblock = NULL;
  kbnode_t node;
  int modified = 0;
  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, 1, &kdbhd, &keyblock);
  if (err)
    goto leave;

  /* To 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. */
  node = find_userid (keyblock, uidtorev, 0);
  if (node)
    {
      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;
        }
      maybe_upload_key (ctrl, keyblock);
      revalidation_mark (ctrl);
      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));
      write_status_error ("keyedit.revoke.uid", 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 primarynode;
  kbnode_t node;

#ifdef HAVE_W32_SYSTEM
  /* See keyedit_menu for why we need this.  */
  check_trustdb_stale (ctrl);
#endif

  err = quick_find_keyblock (ctrl, username, 1, &kdbhd, &keyblock);
  if (err)
    {
      write_status_error ("keyedit.primary", err);
      goto leave;
    }

  /* Find the first matching UID that is valid */
  primarynode = find_userid (keyblock, primaryuid, 1);

  /* and mark it. */
  if (primarynode)
    for (node = keyblock; node; node = node->next)
      {
        if (node == primarynode)
          node->flag |= NODFLG_SELUID;
        else
          node->flag &= ~NODFLG_SELUID;
      }

  if (!primarynode)
    err = gpg_error (GPG_ERR_NO_USER_ID);
  else if (menu_set_primary_uid (ctrl, keyblock))
    {
      merge_keys_and_selfsig (ctrl, keyblock);
      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
      if (err)
        {
          log_error (_("update failed: %s\n"), gpg_strerror (err));
          goto leave;
        }
      maybe_upload_key (ctrl, keyblock);
      revalidation_mark (ctrl);
    }
  else
    err = gpg_error (GPG_ERR_GENERAL);

  if (err)
    {
      log_error (_("setting the primary user ID failed: %s\n"),
                gpg_strerror (err));
      write_status_error ("keyedit.primary", err);
    }

 leave:
  release_kbnode (keyblock);
  keydb_release (kdbhd);
}


/* Unattended updating of the preference tro the standard preferences.
 * USERNAME specifies the key.  This is basically the same as
 *      gpg --edit-key <<userif> updpref save
 */
void
keyedit_quick_update_pref (ctrl_t ctrl, const char *username)
{
  gpg_error_t err;
  KEYDB_HANDLE kdbhd = NULL;
  kbnode_t keyblock = NULL;

#ifdef HAVE_W32_SYSTEM
  /* See keyedit_menu for why we need this.  */
  check_trustdb_stale (ctrl);
#endif

  err = quick_find_keyblock (ctrl, username, 1, &kdbhd, &keyblock);
  if (err)
    goto leave;

  if (menu_set_preferences (ctrl, keyblock, 1))
    {
      merge_keys_and_selfsig (ctrl, keyblock);
      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
      if (err)
        {
          log_error (_("update failed: %s\n"), gpg_strerror (err));
          goto leave;
        }
      maybe_upload_key (ctrl, keyblock);
    }

 leave:
  if (err)
    write_status_error ("keyedit.updpref", err);
  release_kbnode (keyblock);
  keydb_release (kdbhd);
}


/* Unattended updating of the ownertrust or disable/enable state of a key
 * USERNAME specifies the key.  This is somewhat similar to
 *      gpg --edit-key <userid> trust save
 *      gpg --edit-key <userid> disable save
 *
 * VALUE is the new trust value which is one of:
 *    "undefined"  - Ownertrust is set to undefined
 *    "never"      - Ownertrust is set to never trust
 *    "marginal"   - Ownertrust is set to marginal trust
 *    "full"       - Ownertrust is set to full trust
 *    "ultimate"   - Ownertrust is set to ultimate trust
 *    "enable"     - The key is re-enabled.
 *    "disable"    - The key is disabled.
 * Trust settings do not change the ebable/disable state.
 */
void
keyedit_quick_set_ownertrust (ctrl_t ctrl, const char *username,
                              const char *value)
{
  gpg_error_t err;
  KEYDB_HANDLE kdbhd = NULL;
  kbnode_t keyblock = NULL;
  PKT_public_key *pk;
  unsigned int trust, newtrust;
  int x;
  int maybe_update_trust = 0;

#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.  Note
   * that we are looking for the public key here.  */
  err = quick_find_keyblock (ctrl, username, 0, &kdbhd, &keyblock);
  if (err)
    goto leave;
  log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
              || keyblock->pkt->pkttype == PKT_SECRET_KEY);
  pk = keyblock->pkt->pkt.public_key;

  trust = newtrust = get_ownertrust (ctrl, pk);

  if (!ascii_strcasecmp (value, "enable"))
    newtrust &= ~TRUST_FLAG_DISABLED;
  else if (!ascii_strcasecmp (value, "disable"))
    newtrust |= TRUST_FLAG_DISABLED;
  else if ((x = string_to_trust_value (value)) >= 0)
    {
      newtrust = x;
      newtrust &= TRUST_MASK;
      newtrust |= (trust & ~TRUST_MASK);
      maybe_update_trust = 1;
    }
  else
    {
      err = gpg_error (GPG_ERR_INV_ARG);
      goto leave;
    }

  if (trust != newtrust)
    {
      update_ownertrust (ctrl, pk, newtrust);
      if (maybe_update_trust)
        revalidation_mark (ctrl);
    }
  else if (opt.verbose)
    log_info (_("Key not changed so no update needed.\n"));

 leave:
  if (err)
    {
      log_error (_("setting the ownertrust to '%s' failed: %s\n"),
                 value, gpg_strerror (err));
      write_status_error ("keyedit.setownertrust", err);
    }
  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)
    {
      log_error (_("\"%s\" is not a fingerprint\n"), fpr);
      err = gpg_error (GPG_ERR_INV_NAME);
      goto leave;
    }
  err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
                           NULL, NULL, fpr, &keyblock, &kdbhd, 1);
  if (err)
    {
      log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err));
      goto leave;
    }

  /* Check that the primary fingerprint has been given. */
  fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen);
  if (desc.mode == KEYDB_SEARCH_MODE_FPR
      && fprlen == desc.fprlen
      && !memcmp (fprbin, desc.u.fpr, fprlen))
    ;
  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 specified 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.  If
   TRUSTSIG is given a trust signature is created; see
   parse_trustsig_string(). */
void
keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
                    strlist_t locusr, const char *trustsig, int local)
{
  gpg_error_t err = 0;
  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

  /* Do an early check on an arg for an immediate error message.  */
  if (trustsig)
    {
      byte trust_depth, trust_value;
      char *trust_regexp;
      err = parse_trustsig_string (trustsig, &trust_value,
                                   &trust_depth, &trust_regexp);
      xfree (trust_regexp);
      (void)trust_depth;
      (void)trust_value;
      if (err)
        goto leave;
    }

  /* 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 (ctrl, &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"));
      err = gpg_error (GPG_ERR_CERT_REVOKED);
      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"));
      err = gpg_error (GPG_ERR_NO_USER_ID);
      goto leave;
    }

  /* Sign. */
  err = sign_uids (ctrl, es_stdout, keyblock, locusr,
                   (SIGN_UIDS_QUICK
                    | (local? SIGN_UIDS_LOCAL : 0)
                    | (trustsig? SIGN_UIDS_TRUSTSIG : 0)),
                   trustsig, &modified);
  es_fflush (es_stdout);
  if (err)
    goto leave;

  if (modified)
    {
      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
      if (err)
        {
          log_error (_("update failed: %s\n"), gpg_strerror (err));
          goto leave;
        }
      if (!local) /* No need to upload new non-expotable sigs.  */
        maybe_upload_key (ctrl, keyblock);
    }
  else
    log_info (_("Key not changed so no update needed.\n"));

  if (update_trust)
    revalidation_mark (ctrl);

 leave:
  if (err)
    {
      log_error (_("creating key signature failed: %s\n"), gpg_strerror (err));
      write_status_error ("keyedit.sign-key", err);
    }
  release_kbnode (keyblock);
  keydb_release (kdbhd);
}


/* Unattended revocation of a key signatures.  USERNAME specifies the
 * key; this should best be a fingerprint. SIGTOREV is the user-id of
 * the key for which the key signature shall be removed.  Only
 * non-self-signatures can be removed with this functions.  If
 * AFFECTED_UIDS is not NULL only the key signatures on these user-ids
 * are revoked. */
void
keyedit_quick_revsig (ctrl_t ctrl, const char *username, const char *sigtorev,
                      strlist_t affected_uids)
{
  gpg_error_t err = 0;
  int no_signing_key = 0;
  KEYDB_HANDLE kdbhd = NULL;
  kbnode_t keyblock = NULL;
  PKT_public_key *primarypk;  /* Points into KEYBLOCK.  */
  u32 *primarykid;
  PKT_public_key *pksigtorev = NULL;
  u32 *pksigtorevkid;
  kbnode_t node, n;
  int skip_remaining;
  int consider_sig;
  strlist_t sl;
  struct sign_attrib attrib = { 0 };

#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.  Note
   * that we are looking for the public key here.  */
  err = quick_find_keyblock (ctrl, username, 0, &kdbhd, &keyblock);
  if (err)
    goto leave;
  log_assert (keyblock && (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
              || keyblock->pkt->pkttype == PKT_SECRET_KEY));
  primarypk = keyblock->pkt->pkt.public_key;
  primarykid = pk_keyid (primarypk);

  /* Get the signing key we want to revoke.  This must be one of our
   * signing keys.  We will compare only the keyid because we don't
   * assume that we have duplicated keyids on our own secret keys.  If
   * a there is a duplicated one we will notice this when creating the
   * revocation.  */
  pksigtorev = xtrycalloc (1, sizeof *pksigtorev);
  if (!pksigtorev)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  pksigtorev->req_usage = PUBKEY_USAGE_CERT;
  err = getkey_byname (ctrl, NULL, pksigtorev, sigtorev, 1, NULL);
  if (err)
    {
      no_signing_key = 1;
      goto leave;
    }
  pksigtorevkid = pk_keyid (pksigtorev);

  /* Find the signatures we want to revoke and set a mark.  */
  skip_remaining = consider_sig = 0;
  for (node = keyblock; node; node = node->next)
    {
      node->flag &= ~NODFLG_MARK_A;
      if (skip_remaining)
        ;
      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        skip_remaining = 1;
      else if (node->pkt->pkttype == PKT_USER_ID)
	{
          PKT_user_id *uid = node->pkt->pkt.user_id;

          consider_sig = !affected_uids;
          for (sl = affected_uids; !consider_sig && sl; sl = sl->next)
            {
              const char *name = sl->d;

              if (uid->attrib_data)
                ;
              else if (*name == '='
                       && strlen (name+1) == uid->len
                       && !memcmp (uid->name, name + 1, uid->len))
                { /* Exact match.  */
                  consider_sig = 1;
                }
              else if (ascii_memistr (uid->name, uid->len,
                                      *name == '*'? name+1:name))
                { /* Case-insensitive substring match.  */
                  consider_sig = 1;
                }
            }
	}
      else if (node->pkt->pkttype == PKT_SIGNATURE)
	{
          /* We need to sort the signatures so that we can figure out
           * whether the key signature has been revoked or the
           * revocation has been superseded by a new key
           * signature.  */
          PKT_signature *sig;
          unsigned int sigcount = 0;
          kbnode_t *sigarray;

          /* Allocate an array large enough for all signatures.  */
          for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
            sigcount++;
          sigarray = xtrycalloc (sigcount, sizeof *sigarray);
          if (!sigarray)
            {
              err = gpg_error_from_syserror ();
              goto leave;
            }

          /* Now fill the array with signatures we are interested in.
           * We also move NODE forward to the end.  */
          sigcount = 0;
          for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; node=n, n=n->next)
            {
              sig = n->pkt->pkt.signature;
              if (!keyid_cmp (primarykid, sig->keyid))
                continue;  /* Ignore self-signatures.  */

              if (keyid_cmp (pksigtorevkid, sig->keyid))
                continue; /* Ignore non-matching signatures.  */

              n->flag &= ~NODFLG_MARK_B; /* Clear flag used by cm_signode. */
              sigarray[sigcount++] = n;
            }

          if (sigcount)
            {
              qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes);

              /* log_debug ("Sorted signatures:\n"); */
              /* for (idx=0; idx < sigcount; idx++) */
              /*   { */
              /*     sig = sigarray[idx]->pkt->pkt.signature; */
              /*     log_debug ("%s 0x%02x %s\n", keystr (sig->keyid), */
              /*                sig->sig_class, datestr_from_sig (sig)); */
              /*   } */
              sig = sigarray[sigcount-1]->pkt->pkt.signature;
              if ((consider_sig || !affected_uids) && IS_UID_REV (sig))
                {
                  if (!opt.quiet)
                    log_info ("sig by %s already revoked at %s\n",
                              keystr (sig->keyid), datestr_from_sig (sig));
                }
              else if ((consider_sig && IS_UID_SIG (sig))
                       || (!affected_uids && IS_KEY_SIG (sig)))
                node->flag |= NODFLG_MARK_A;  /* Select signature.  */
            }

          xfree (sigarray);
	}
    }

  /* Check whether any signatures were done by the given key.  We do
   * not return an error if none were found.  */
  for (node = keyblock; node; node = node->next)
    if ((node->flag & NODFLG_MARK_A))
      break;
  if (!node)
    {
      if (opt.verbose)
        log_info (_("Not signed by you.\n"));
      err = 0;
      goto leave;
    }

  /* Revoke all marked signatures.  */
  attrib.reason = get_default_sig_revocation_reason ();

 reloop: /* (we must repeat because we are modifying the list) */
  for (node = keyblock; node; node = node->next)
    {
      kbnode_t unode;
      PKT_signature *sig;
      PACKET *pkt;

      if (!(node->flag & NODFLG_MARK_A))
	continue;
      node->flag &= ~NODFLG_MARK_A;

      if (IS_KEY_SIG (node->pkt->pkt.signature))
        unode = NULL;
      else
        {
          unode = find_prev_kbnode (keyblock, node, PKT_USER_ID);
          log_assert (unode);
        }

      attrib.non_exportable = !node->pkt->pkt.signature->flags.exportable;

      err = make_keysig_packet (ctrl, &sig, primarypk,
                                unode? unode->pkt->pkt.user_id : NULL,
                                NULL, pksigtorev, 0x30, 0, 0,
                                sign_mk_attrib, &attrib, NULL);
      if (err)
        {
          log_error ("signing failed: %s\n", gpg_strerror (err));
          goto leave;
        }

      pkt = xmalloc_clear (sizeof *pkt);
      pkt->pkttype = PKT_SIGNATURE;
      pkt->pkt.signature = sig;
      if (unode)
        insert_kbnode (unode, new_kbnode (pkt), 0);
      goto reloop;
    }

  err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
  if (err)
    {
      log_error (_("update failed: %s\n"), gpg_strerror (err));
      goto leave;
    }
  maybe_upload_key (ctrl, keyblock);
  revalidation_mark (ctrl);

 leave:
  if (err)
    {
      log_error (_("revoking the key signature failed: %s\n"),
                 gpg_strerror (err));
      if (no_signing_key)
        print_further_info ("error getting key used to make the key signature");
      write_status_error ("keyedit.revoke.sig", err);
    }
  release_revocation_reason_info (attrib.reason);
  free_public_key (pksigtorev);
  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 ((err=find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)))
    goto leave;

  if (fix_keyblock (ctrl, &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");
      err = gpg_error (GPG_ERR_CERT_REVOKED);
      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;
        }
      maybe_upload_key (ctrl, keyblock);
    }
  else
    log_info (_("Key not changed so no update needed.\n"));

 leave:
  if (err)
    write_status_error ("keyedit.addkey", err);
  release_kbnode (keyblock);
  keydb_release (kdbhd);
}


/* Unattended ADSK setup function.
 *
 * FPR is the fingerprint of our key.  ADSKFPR is the fingerprint of
 * another subkey which we want to add as ADSK to our key.
 */
void
keyedit_quick_addadsk (ctrl_t ctrl, const char *fpr, const char *adskfpr)
{
  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 adsk
   * adding. */
  if ((err = find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)))
    goto leave;

  if (fix_keyblock (ctrl, &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");
      err = gpg_error (GPG_ERR_CERT_REVOKED);
      goto leave;
    }

  /* Locate and add the ADSK.  Note that the called function already
   * prints error messages. */
  if (adskfpr && !ascii_strcasecmp (adskfpr, "default"))
    {
      err = append_all_default_adsks (ctrl, keyblock);
      if (!err)
        modified = 1;
      else if (gpg_err_code (err) == GPG_ERR_FALSE)
        err = 0;
    }
  else if (menu_addadsk (ctrl, keyblock, adskfpr))
    modified = 1;
  else
    log_inc_errorcount ();  /* (We use log_info in menu_adsk) */

  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;
        }
      maybe_upload_key (ctrl, keyblock);
    }

 leave:
  if (err)
    write_status_error ("keyedit.addadsk", err);
  release_kbnode (keyblock);
  keydb_release (kdbhd);
}


/* Unattended expiration setting function for the main key.  If
 * SUBKEYFPRS is not NULL and SUBKEYSFPRS[0] is neither NULL, it is
 * expected to be an array of fingerprints for subkeys to change. It
 * may also be an array with only the item "*" to indicate that all
 * keys shall be set to that expiration date.
 */
void
keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr,
                          char **subkeyfprs)
{
  gpg_error_t err;
  kbnode_t keyblock, node;
  KEYDB_HANDLE kdbhd;
  int modified = 0;
  PKT_public_key *pk;
  u32 expire;
  int primary_only = 0;
  int idx;

#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 (ctrl, &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");
      err = gpg_error (GPG_ERR_CERT_REVOKED);
      goto leave;
    }

  expire = parse_expire_string (expirestr);
  if (expire == (u32)-1 )
    {
      log_error (_("'%s' is not a valid expiration time\n"), expirestr);
      err = gpg_error (GPG_ERR_INV_VALUE);
      goto leave;
    }
  if (expire)
    expire += make_timestamp ();

  /* Check whether a subkey's expiration time shall be changed or the
   * expiration time of all keys.  */
  if (!subkeyfprs || !subkeyfprs[0])
    primary_only = 1;
  else if ( !strcmp (subkeyfprs[0], "*") && !subkeyfprs[1])
    {
      /* Change all subkeys keys which have not been revoked and are
       * not yet expired.  */
      merge_keys_and_selfsig (ctrl, keyblock);
      for (node = keyblock; node; node = node->next)
        {
          if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
              && (pk = node->pkt->pkt.public_key)
              && !pk->flags.revoked
              && !pk->has_expired)
            node->flag |= NODFLG_SELKEY;
        }
    }
  else
    {
      /* Change specified subkeys.  */
      KEYDB_SEARCH_DESC desc;
      byte fprbin[MAX_FINGERPRINT_LEN];
      size_t fprlen;

      err = 0;
      merge_keys_and_selfsig (ctrl, keyblock);
      for (idx=0; subkeyfprs[idx]; idx++)
        {
          int any = 0;

          /* Parse the fingerprint.  */
          if (classify_user_id (subkeyfprs[idx], &desc, 1)
              || desc.mode != KEYDB_SEARCH_MODE_FPR)
            {
              log_error (_("\"%s\" is not a proper fingerprint\n"),
                         subkeyfprs[idx] );
              if (!err)
                err = gpg_error (GPG_ERR_INV_NAME);
              continue;
            }

          /* Set the flag for the matching non revoked subkey.  */
          for (node = keyblock; node; node = node->next)
            {
              if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
                  && (pk = node->pkt->pkt.public_key)
                  && !pk->flags.revoked )
                {
                  fingerprint_from_pk (pk, fprbin, &fprlen);
                  if (fprlen == desc.fprlen && !memcmp (fprbin, desc.u.fpr, fprlen))
                    {
                      node->flag |= NODFLG_SELKEY;
                      any = 1;
                    }
                }
            }
          if (!any)
            {
              log_error (_("subkey \"%s\" not found\n"), subkeyfprs[idx]);
              if (!err)
                err = gpg_error (GPG_ERR_NOT_FOUND);
            }
        }

      if (err)
        goto leave;
    }

  /* Set the new expiration date.  */
  err = menu_expire (ctrl, keyblock, primary_only? 1 : 2, expire);
  if (gpg_err_code (err) == GPG_ERR_TRUE)
    modified = 1;
  else if (err)
    goto leave;
  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;
        }
      maybe_upload_key (ctrl, keyblock);
      if (update_trust)
        revalidation_mark (ctrl);
    }
  else
    log_info (_("Key not changed so no update needed.\n"));

 leave:
  release_kbnode (keyblock);
  keydb_release (kdbhd);
  if (err)
    write_status_error ("set_expire", err);
}



static void
tty_print_notations (int indent, PKT_signature * sig)
{
  int first = 1;
  struct notation *notation, *nd;

  if (indent < 0)
    {
      first = 0;
      indent = -indent;
    }

  notation = sig_to_notation (sig);

  for (nd = notation; nd; nd = nd->next)
    {
      if (!first)
	tty_printf ("%*s", indent, "");
      else
	first = 0;

      tty_print_utf8_string (nd->name, strlen (nd->name));
      tty_printf ("=");
      tty_print_utf8_string (nd->value, strlen (nd->value));
      tty_printf ("\n");
    }

  free_notation (notation);
}


/*
 * Show preferences of a public keyblock.
 */
static void
show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose)
{
  if (!uid)
    return;

  if (verbose)
    {
      show_preferences (uid, 4, -1, 1);

      if (selfsig)
	{
	  const byte *pref_ks;
	  size_t pref_ks_len;

	  pref_ks = parse_sig_subpkt (selfsig, 1,
                                      SIGSUBPKT_PREF_KS, &pref_ks_len);
	  if (pref_ks && pref_ks_len)
	    {
	      tty_printf ("     ");
	      tty_printf (_("Preferred keyserver: "));
	      tty_print_utf8_string (pref_ks, pref_ks_len);
	      tty_printf ("\n");
	    }

	  if (selfsig->flags.notation)
	    {
	      tty_printf ("     ");
	      tty_printf (_("Notations: "));
	      tty_print_notations (5 + strlen (_("Notations: ")), selfsig);
	    }
	}
    }
  else
    {
      show_preferences (uid, 4, -1, 0);
    }
}


/* This is the version of show_key_with_all_names used when
   opt.with_colons is used.  It prints all available data in a easy to
   parse format and does not translate utf8 */
static void
show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
{
  KBNODE node;
  int i, j, ulti_hack = 0;
  byte pk_version = 0;
  PKT_public_key *primary = NULL;
  int have_seckey;

  if (!fp)
    fp = es_stdout;

  /* the keys */
  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY
	  || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY))
	{
	  PKT_public_key *pk = node->pkt->pkt.public_key;
	  u32 keyid[2];

	  if (node->pkt->pkttype == PKT_PUBLIC_KEY)
	    {
	      pk_version = pk->version;
	      primary = pk;
	    }

	  keyid_from_pk (pk, keyid);
          have_seckey = agent_probe_secret_key (ctrl, pk);

          if (node->pkt->pkttype == PKT_PUBLIC_KEY)
            es_fputs (have_seckey? "sec:" : "pub:", fp);
          else
            es_fputs (have_seckey? "ssb:" : "sub:", fp);

	  if (!pk->flags.valid)
	    es_putc ('i', fp);
	  else if (pk->flags.revoked)
	    es_putc ('r', fp);
	  else if (pk->has_expired)
	    es_putc ('e', fp);
	  else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks))
	    {
	      int trust = get_validity_info (ctrl, keyblock, pk, NULL);
	      if (trust == 'u')
		ulti_hack = 1;
	      es_putc (trust, fp);
	    }

	  es_fprintf (fp, ":%u:%d:%08lX%08lX:%lu:%lu::",
                      nbits_from_pk (pk),
                      pk->pubkey_algo,
                      (ulong) keyid[0], (ulong) keyid[1],
                      (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 (ctrl, pk, 0), fp);
	  es_putc (':', fp);
	  es_putc (':', fp);
	  es_putc (':', fp);
	  /* Print capabilities.  */
	  if ((pk->pubkey_usage & PUBKEY_USAGE_ENC))
	    es_putc ('e', fp);
	  if ((pk->pubkey_usage & PUBKEY_USAGE_SIG))
	    es_putc ('s', fp);
	  if ((pk->pubkey_usage & PUBKEY_USAGE_CERT))
	    es_putc ('c', fp);
	  if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH))
	    es_putc ('a', fp);
	  if ((pk->pubkey_usage & PUBKEY_USAGE_RENC))
	    es_putc ('r', fp);
	  if ((pk->pubkey_usage & PUBKEY_USAGE_TIME))
	    es_putc ('t', fp);
	  if ((pk->pubkey_usage & PUBKEY_USAGE_GROUP))
	    es_putc ('g', fp);
	  es_putc ('\n', fp);

	  print_fingerprint (ctrl, fp, pk, 0);
	  print_revokers (fp, 1, pk);
	}
    }

  /* the user ids */
  i = 0;
  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  PKT_user_id *uid = node->pkt->pkt.user_id;

	  ++i;

	  if (uid->attrib_data)
	    es_fputs ("uat:", fp);
	  else
	    es_fputs ("uid:", fp);

	  if (uid->flags.revoked)
	    es_fputs ("r::::::::", fp);
	  else if (uid->flags.expired)
	    es_fputs ("e::::::::", fp);
	  else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
	    es_fputs ("::::::::", fp);
	  else
	    {
	      int uid_validity;

	      if (primary && !ulti_hack)
		uid_validity = get_validity_info (ctrl, keyblock, primary, uid);
	      else
		uid_validity = 'u';
	      es_fprintf (fp, "%c::::::::", uid_validity);
	    }

	  if (uid->attrib_data)
	    es_fprintf (fp, "%u %lu", uid->numattribs, uid->attrib_len);
	  else
	    es_write_sanitized (fp, uid->name, uid->len, ":", NULL);

	  es_putc (':', fp);
	  /* signature class */
	  es_putc (':', fp);
	  /* capabilities */
	  es_putc (':', fp);
	  /* preferences */
	  if (pk_version > 3 || uid->selfsigversion > 3)
	    {
	      const prefitem_t *prefs = uid->prefs;

	      for (j = 0; prefs && prefs[j].type; j++)
		{
		  if (j)
		    es_putc (' ', fp);
		  es_fprintf (fp,
                              "%c%d", prefs[j].type == PREFTYPE_SYM ? 'S' :
                              prefs[j].type == PREFTYPE_HASH ? 'H' :
                              prefs[j].type == PREFTYPE_ZIP ? 'Z' : '?',
                              prefs[j].value);
		}
	      if (uid->flags.mdc)
		es_fputs (",mdc", fp);
	      if (uid->flags.aead)
		es_fputs (",aead", fp);
	      if (!uid->flags.ks_modify)
		es_fputs (",no-ks-modify", fp);
	    }
	  es_putc (':', fp);
	  /* flags */
	  es_fprintf (fp, "%d,", i);
	  if (uid->flags.primary)
	    es_putc ('p', fp);
	  if (uid->flags.revoked)
	    es_putc ('r', fp);
	  if (uid->flags.expired)
	    es_putc ('e', fp);
	  if ((node->flag & NODFLG_SELUID))
	    es_putc ('s', fp);
	  if ((node->flag & NODFLG_MARK_A))
	    es_putc ('m', fp);
	  es_putc (':', fp);
	  if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
	    {
#ifdef USE_TOFU
	      enum tofu_policy policy;
	      if (! tofu_get_policy (ctrl, primary, uid, &policy)
		  && policy != TOFU_POLICY_NONE)
		es_fprintf (fp, "%s", tofu_policy_str (policy));
#endif /*USE_TOFU*/
	    }
	  es_putc (':', fp);
	  es_putc ('\n', fp);
	}
    }
}


static void
show_names (ctrl_t ctrl, estream_t fp,
            kbnode_t keyblock, PKT_public_key * pk, unsigned int flag,
	    int with_prefs)
{
  KBNODE node;
  int i = 0;

  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID && !is_deleted_kbnode (node))
	{
	  PKT_user_id *uid = node->pkt->pkt.user_id;
	  ++i;
	  if (!flag || (flag && (node->flag & flag)))
	    {
	      if (!(flag & NODFLG_MARK_A) && pk)
		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);
	      else if (uid->flags.primary)
		tty_fprintf (fp, "(%d). ", i);
	      else
		tty_fprintf (fp, "(%d)  ", i);
	      tty_print_utf8_string2 (fp, uid->name, uid->len, 0);
	      tty_fprintf (fp, "\n");
	      if (with_prefs && pk)
		{
		  if (pk->version > 3 || uid->selfsigversion > 3)
		    {
		      PKT_signature *selfsig = NULL;
		      KBNODE signode;

		      for (signode = node->next;
			   signode && signode->pkt->pkttype == PKT_SIGNATURE;
			   signode = signode->next)
			{
			  if (signode->pkt->pkt.signature->
			      flags.chosen_selfsig)
			    {
			      selfsig = signode->pkt->pkt.signature;
			      break;
			    }
			}

		      show_prefs (uid, selfsig, with_prefs == 2);
		    }
		  else
		    tty_fprintf (fp, _("There are no preferences on a"
                                       " PGP 2.x-style user ID.\n"));
		}
	    }
	}
    }
}


/*
 * Display the key a the user ids, if only_marked is true, do only so
 * for user ids with mark A flag set and do not display the index
 * number.  If FP is not NULL print to the given stream and not to the
 * tty (ignored in with-colons mode).
 */
static void
show_key_with_all_names (ctrl_t ctrl, estream_t fp,
                         KBNODE keyblock, int only_marked, int with_revoker,
			 int with_fpr, int with_subkeys, int with_prefs,
                         int nowarn)
{
  gpg_error_t err;
  kbnode_t node;
  int i;
  int do_warn = 0;
  int have_seckey = 0;
  char *serialno = NULL;
  PKT_public_key *primary = NULL;
  char pkstrbuf[PUBKEY_STRING_SIZE];

  if (opt.with_colons)
    {
      show_key_with_all_names_colon (ctrl, fp, keyblock);
      return;
    }

  /* the keys */
  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY
	  || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY
	      && !is_deleted_kbnode (node)))
	{
	  PKT_public_key *pk = node->pkt->pkt.public_key;
	  const char *otrust = "err";
	  const char *trust = "err";

	  if (node->pkt->pkttype == PKT_PUBLIC_KEY)
	    {
	      /* do it here, so that debug messages don't clutter the
	       * output */
	      static int did_warn = 0;

	      trust = get_validity_string (ctrl, pk, NULL);
	      otrust = get_ownertrust_string (ctrl, pk, 0);

	      /* Show a warning once */
	      if (!did_warn
		  && (get_validity (ctrl, keyblock, pk, NULL, NULL, 0)
		      & TRUST_FLAG_PENDING_CHECK))
		{
		  did_warn = 1;
		  do_warn = 1;
		}

	      primary = pk;
	    }

	  if (pk->flags.revoked)
	    {
	      char *user = get_user_id_string_native (ctrl, pk->revoked.keyid);
              tty_fprintf (fp,
                           _("The following key was revoked on"
                            " %s by %s key %s\n"),
			  revokestr_from_pk (pk),
                          openpgp_pk_algo_name (pk->revoked.algo), user);
	      xfree (user);
	    }

	  if (with_revoker)
	    {
	      if (!pk->revkey && pk->numrevkeys)
		BUG ();
	      else
		for (i = 0; i < pk->numrevkeys; i++)
		  {
		    u32 r_keyid[2];
		    char *user;
		    const char *algo;

		    algo = openpgp_pk_algo_name (pk->revkey[i].algid);
		    keyid_from_fingerprint (ctrl, pk->revkey[i].fpr,
					    pk->revkey[i].fprlen, r_keyid);

		    user = get_user_id_string_native (ctrl, r_keyid);
		    tty_fprintf (fp,
                                 _("This key may be revoked by %s key %s"),
                                 algo, user);

		    if (pk->revkey[i].class & 0x40)
		      {
			tty_fprintf (fp, " ");
			tty_fprintf (fp, _("(sensitive)"));
		      }

		    tty_fprintf (fp, "\n");
		    xfree (user);
		  }
	    }

	  keyid_from_pk (pk, NULL);

          xfree (serialno);
          serialno = NULL;
          {
            char *hexgrip;

            err = hexkeygrip_from_pk (pk, &hexgrip);
            if (err)
              {
                log_error ("error computing a keygrip: %s\n",
                           gpg_strerror (err));
                have_seckey = 0;
              }
            else
              have_seckey = !agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL);
            xfree (hexgrip);
          }

	  tty_fprintf
            (fp, "%s%c %s/%s",
             node->pkt->pkttype == PKT_PUBLIC_KEY && have_seckey? "sec" :
             node->pkt->pkttype == PKT_PUBLIC_KEY ?               "pub" :
             have_seckey ?                                        "ssb" :
                                                                  "sub",
             (node->flag & NODFLG_SELKEY) ? '*' : ' ',
             pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
             keystr (pk->keyid));

          if (opt.legacy_list_mode)
            tty_fprintf (fp, "  ");
          else
            tty_fprintf (fp, "\n     ");

          tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk));
	  tty_fprintf (fp, "  ");
	  if (pk->flags.revoked)
	    tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk));
	  else if (pk->has_expired)
	    tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk));
	  else
	    tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
	  tty_fprintf (fp, "  ");
	  tty_fprintf (fp, _("usage: %s"), usagestr_from_pk (pk, 1));
	  tty_fprintf (fp, "\n");

          if (serialno)
            {
              /* The agent told us that a secret key is available and
                 that it has been stored on a card.  */
	      tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "",
                           _("card-no: "));
              if (strlen (serialno) == 32
                  && !strncmp (serialno, "D27600012401", 12))
                {
                  /* This is an OpenPGP card.  Print the relevant part.  */
                  /* Example: D2760001240101010001000003470000 */
                  /*                          xxxxyyyyyyyy     */
                  tty_fprintf (fp, "%.*s %.*s\n",
                               4, serialno+16, 8, serialno+20);
                }
              else
                tty_fprintf (fp, "%s\n", serialno);

            }
	  else if (pk->seckey_info
              && pk->seckey_info->is_protected
              && pk->seckey_info->s2k.mode == 1002)
	    {
              /* FIXME: Check whether this code path is still used.  */
	      tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "",
                           _("card-no: "));
	      if (pk->seckey_info->ivlen == 16
		  && !memcmp (pk->seckey_info->iv,
                              "\xD2\x76\x00\x01\x24\x01", 6))
		{
                  /* This is an OpenPGP card. */
		  for (i = 8; i < 14; i++)
		    {
		      if (i == 10)
			tty_fprintf (fp, " ");
		      tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]);
		    }
		}
	      else
		{
                  /* Unknown card: Print all. */
		  for (i = 0; i < pk->seckey_info->ivlen; i++)
		    tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]);
		}
	      tty_fprintf (fp, "\n");
	    }

	  if (node->pkt->pkttype == PKT_PUBLIC_KEY
              || node->pkt->pkttype == PKT_SECRET_KEY)
	    {
	      if (opt.trust_model != TM_ALWAYS)
		{
		  tty_fprintf (fp, "%*s",
                               opt.legacy_list_mode?
                               ((int) keystrlen () + 13):5, "");
		  /* Ownertrust is only meaningful for the PGP or
		     classic trust models, or PGP combined with TOFU */
		  if (opt.trust_model == TM_PGP
		      || opt.trust_model == TM_CLASSIC
		      || opt.trust_model == TM_TOFU_PGP)
		    {
		      int width = 14 - strlen (otrust);
		      if (width <= 0)
			width = 1;
		      tty_fprintf (fp, _("trust: %s"), otrust);
		      tty_fprintf (fp, "%*s", width, "");
		    }

		  tty_fprintf (fp, _("validity: %s"), trust);
		  tty_fprintf (fp, "\n");
		}
	      if (node->pkt->pkttype == PKT_PUBLIC_KEY
		  && (get_ownertrust (ctrl, pk) & TRUST_FLAG_DISABLED))
		{
		  tty_fprintf (fp, "*** ");
		  tty_fprintf (fp, _("This key has been disabled"));
		  tty_fprintf (fp, "\n");
		}
	    }

	  if ((node->pkt->pkttype == PKT_PUBLIC_KEY
               || node->pkt->pkttype == PKT_SECRET_KEY) && with_fpr)
	    {
              print_fingerprint (ctrl, fp, pk, 2);
	      tty_fprintf (fp, "\n");
	    }
	}
    }

  show_names (ctrl, fp,
              keyblock, primary, only_marked ? NODFLG_MARK_A : 0, with_prefs);

  if (do_warn && !nowarn)
    tty_fprintf (fp, _("Please note that the shown key validity"
                       " is not necessarily correct\n"
                       "unless you restart the program.\n"));

  xfree (serialno);
}


/* Display basic key information.  This function is suitable to show
 * information on the key without any dependencies on the trustdb or
 * any other internal GnuPG stuff.  KEYBLOCK may either be a public or
 * a secret key.  This function may be called with KEYBLOCK containing
 * secret keys and thus the printing of "pub" vs. "sec" does only
 * depend on the packet type and not by checking with gpg-agent.  If
 * PRINT_SEC is set "sec" is printed instead of "pub".  */
void
show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock, int print_sec)
{
  KBNODE node;
  int i;
  char pkstrbuf[PUBKEY_STRING_SIZE];

  /* The primary key */
  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY
          || node->pkt->pkttype == PKT_SECRET_KEY)
	{
	  PKT_public_key *pk = node->pkt->pkt.public_key;
          const char *tag;

          if (node->pkt->pkttype == PKT_SECRET_KEY || print_sec)
            tag = "sec";
          else
            tag = "pub";

	  /* Note, we use the same format string as in other show
	     functions to make the translation job easier. */
	  tty_printf ("%s  %s/%s  ",
                      tag,
                      pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
		      keystr_from_pk (pk));
	  tty_printf (_("created: %s"), datestr_from_pk (pk));
	  tty_printf ("  ");
	  tty_printf (_("expires: %s"), expirestr_from_pk (pk));
	  tty_printf ("\n");
	  print_fingerprint (ctrl, NULL, pk, 3);
	  tty_printf ("\n");
	}
    }

  /* The user IDs. */
  (void)i; /* Counting User IDs */
  for (i = 0, node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  PKT_user_id *uid = node->pkt->pkt.user_id;
	  ++i;

	  tty_printf ("     ");
	  if (uid->flags.revoked)
	    tty_printf ("[%s] ", _("revoked"));
	  else if (uid->flags.expired)
	    tty_printf ("[%s] ", _("expired"));
	  tty_print_utf8_string (uid->name, uid->len);
	  tty_printf ("\n");
	}
    }
}


static void
show_key_and_fingerprint (ctrl_t ctrl, kbnode_t keyblock, int with_subkeys)
{
  kbnode_t node;
  PKT_public_key *pk = NULL;
  char pkstrbuf[PUBKEY_STRING_SIZE];

  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY)
	{
	  pk = node->pkt->pkt.public_key;
	  tty_printf ("pub   %s/%s %s ",
                      pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
                      keystr_from_pk(pk),
                      datestr_from_pk (pk));
	}
      else if (node->pkt->pkttype == PKT_USER_ID)
	{
	  PKT_user_id *uid = node->pkt->pkt.user_id;
	  tty_print_utf8_string (uid->name, uid->len);
	  break;
	}
    }
  tty_printf ("\n");
  if (pk)
    print_fingerprint (ctrl, NULL, pk, 2);
  if (with_subkeys)
    {
      for (node = keyblock; node; node = node->next)
        {
          if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
            {
              pk = node->pkt->pkt.public_key;
              tty_printf ("sub   %s/%s %s [%s]\n",
                          pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
                          keystr_from_pk(pk),
                          datestr_from_pk (pk),
                          usagestr_from_pk (pk, 0));

              print_fingerprint (ctrl, NULL, pk, 4);
            }
        }
    }
}


/* Show a listing of the primary and its subkeys along with their
   keygrips.  */
static void
show_key_and_grip (kbnode_t keyblock)
{
  kbnode_t node;
  PKT_public_key *pk = NULL;
  char pkstrbuf[PUBKEY_STRING_SIZE];
  char *hexgrip;

  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY
          || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
          pk = node->pkt->pkt.public_key;
          tty_printf ("%s   %s/%s %s [%s]\n",
                      node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
                      pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
                      keystr_from_pk(pk),
                      datestr_from_pk (pk),
                      usagestr_from_pk (pk, 0));

          if (!hexkeygrip_from_pk (pk, &hexgrip))
            {
              tty_printf ("      Keygrip: %s\n", hexgrip);
              xfree (hexgrip);
            }
        }
    }
}


/* Show a warning if no uids on the key have the primary uid flag
   set. */
static void
no_primary_warning (KBNODE keyblock)
{
  KBNODE node;
  int have_primary = 0, uid_count = 0;

  /* TODO: if we ever start behaving differently with a primary or
     non-primary attribute ID, we will need to check for attributes
     here as well. */

  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID
	  && node->pkt->pkt.user_id->attrib_data == NULL)
	{
	  uid_count++;

	  if (node->pkt->pkt.user_id->flags.primary == 2)
	    {
	      have_primary = 1;
	      break;
	    }
	}
    }

  if (uid_count > 1 && !have_primary)
    log_info (_
	      ("WARNING: no user ID has been marked as primary.  This command"
	       " may\n              cause a different user ID to become"
	       " the assumed primary.\n"));
}


/* Print a warning if the latest encryption subkey expires soon.  This
   function is called after the expire data of the primary key has
   been changed.  */
static void
subkey_expire_warning (kbnode_t keyblock)
{
  u32 curtime = make_timestamp ();
  kbnode_t node;
  PKT_public_key *pk;
  /* u32 mainexpire = 0; */
  u32 subexpire = 0;
  u32 latest_date = 0;

  for (node = keyblock; node; node = node->next)
    {
      /* if (node->pkt->pkttype == PKT_PUBLIC_KEY) */
      /*   { */
      /*     pk = node->pkt->pkt.public_key; */
      /*     mainexpire = pk->expiredate; */
      /*   } */

      if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY)
        continue;
      pk = node->pkt->pkt.public_key;

      if (!pk->flags.valid)
        continue;
      if (pk->flags.revoked)
        continue;
      if (pk->timestamp > curtime)
        continue; /* Ignore future keys.  */
      if (!(pk->pubkey_usage & PUBKEY_USAGE_ENC))
        continue; /* Not an encryption key.  */

      if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date))
        {
          latest_date = pk->timestamp;
          subexpire = pk->expiredate;
        }
    }

  if (!subexpire)
    return;  /* No valid subkey with an expiration time.  */

  if (curtime + (10*86400) > subexpire)
    {
      log_info (_("WARNING: Your encryption subkey expires soon.\n"));
      log_info (_("You may want to change its expiration date too.\n"));
    }
}


/* Print a warning if all encryption (sub|primary)keys are expired.
 * The warning is not printed if there is no encryption
 * (sub|primary)key at all.  This function is called after the expire
 * data of the primary key has been changed.  */
void
no_usable_encr_subkeys_warning (kbnode_t keyblock)
{
  kbnode_t node;
  PKT_public_key *pk;
  int any_encr_key = 0;

  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY
          || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
          pk = node->pkt->pkt.public_key;
          if ((pk->pubkey_usage & PUBKEY_USAGE_ENC))
            {
              any_encr_key = 1;
              if (pk->flags.valid && !pk->has_expired && !pk->flags.revoked
                  && !pk->flags.disabled)
                {
                  return; /* Key is usable for encryption  */
                }
            }
        }
    }

  if (any_encr_key && !opt.quiet)
    log_info (_("WARNING: No valid encryption subkey left over.\n"));
}


/*
 * Ask for a new user id, add the self-signature, and update the
 * keyblock.  If UIDSTRING is not NULL the user ID is generated
 * unattended using that string.  UIDSTRING is expected to be utf-8
 * encoded and white space trimmed.  Returns true if there is a new
 * user id.
 */
static int
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_signature *sig = NULL;
  PACKET *pkt;
  KBNODE node;
  KBNODE pub_where = NULL;
  gpg_error_t err;

  if (photo && uidstring)
    return 0;  /* Not allowed.  */

  for (node = pub_keyblock; node; pub_where = node, node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY)
	pk = node->pkt->pkt.public_key;
      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	break;
    }
  if (!node) /* No subkey.  */
    pub_where = NULL;
  log_assert (pk);

  if (photo)
    {
      int hasattrib = 0;

      for (node = pub_keyblock; node; node = node->next)
	if (node->pkt->pkttype == PKT_USER_ID &&
	    node->pkt->pkt.user_id->attrib_data != NULL)
	  {
	    hasattrib = 1;
	    break;
	  }

      /* It is legal but bad for compatibility to add a photo ID to a
         v3 key as it means that PGP2 will not be able to use that key
         anymore.  Also, PGP may not expect a photo on a v3 key.
         Don't bother to ask this if the key already has a photo - any
         damage has already been done at that point. -dms */
      if (pk->version == 3 && !hasattrib)
	{
	  if (opt.expert)
	    {
	      tty_printf (_("WARNING: This is a PGP2-style key.  "
			    "Adding a photo ID may cause some versions\n"
			    "         of PGP to reject this key.\n"));

	      if (!cpr_get_answer_is_yes ("keyedit.v3_photo.okay",
					  _("Are you sure you still want "
					    "to add it? (y/N) ")))
		return 0;
	    }
	  else
	    {
	      tty_printf (_("You may not add a photo ID to "
			    "a PGP2-style key.\n"));
	      return 0;
	    }
	}

      uid = generate_photo_id (ctrl, pk, photo_name);
    }
  else
    uid = generate_user_id (pub_keyblock, uidstring);
  if (!uid)
    {
      if (uidstring)
        {
          write_status_error ("adduid", gpg_error (304));
          log_error ("%s\n", _("Such a user ID already exists on this key!"));
        }
      return 0;
    }

  err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x13, 0, 0,
                            keygen_add_std_prefs, pk, NULL);
  if (err)
    {
      write_status_error ("keysig", err);
      log_error ("signing failed: %s\n", gpg_strerror (err));
      free_user_id (uid);
      return 0;
    }

  /* Insert/append to public keyblock */
  pkt = xmalloc_clear (sizeof *pkt);
  pkt->pkttype = PKT_USER_ID;
  pkt->pkt.user_id = uid;
  node = new_kbnode (pkt);
  if (pub_where)
    insert_kbnode (pub_where, node, 0);
  else
    add_kbnode (pub_keyblock, node);
  pkt = xmalloc_clear (sizeof *pkt);
  pkt->pkttype = PKT_SIGNATURE;
  pkt->pkt.signature = sig;
  if (pub_where)
    insert_kbnode (node, new_kbnode (pkt), 0);
  else
    add_kbnode (pub_keyblock, new_kbnode (pkt));
  return 1;
}


/*
 * Remove all selected userids from the keyring
 */
static void
menu_deluid (KBNODE pub_keyblock)
{
  KBNODE node;
  int selected = 0;

  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  selected = node->flag & NODFLG_SELUID;
	  if (selected)
	    {
	      /* Only cause a trust update if we delete a
	         non-revoked user id */
	      if (!node->pkt->pkt.user_id->flags.revoked)
		update_trust = 1;
	      delete_kbnode (node);
	    }
	}
      else if (selected && node->pkt->pkttype == PKT_SIGNATURE)
	delete_kbnode (node);
      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	selected = 0;
    }
  commit_kbnode (&pub_keyblock);
}


static int
menu_delsig (ctrl_t ctrl, kbnode_t pub_keyblock)
{
  KBNODE node;
  PKT_user_id *uid = NULL;
  int changed = 0;

  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  uid = (node->flag & NODFLG_SELUID) ? node->pkt->pkt.user_id : NULL;
	}
      else if (uid && node->pkt->pkttype == PKT_SIGNATURE)
	{
	  int okay, valid, selfsig, inv_sig, no_key, other_err;

	  tty_printf ("uid  ");
	  tty_print_utf8_string (uid->name, uid->len);
	  tty_printf ("\n");

	  okay = inv_sig = no_key = other_err = 0;
	  if (opt.with_colons)
	    valid = print_and_check_one_sig_colon (ctrl, pub_keyblock, node,
						   &inv_sig, &no_key,
						   &other_err, &selfsig, 1);
	  else
	    valid = print_and_check_one_sig (ctrl, pub_keyblock, node,
					     &inv_sig, &no_key, &other_err,
					     &selfsig, 1, 0);

	  if (valid)
	    {
	      okay = cpr_get_answer_yes_no_quit
                ("keyedit.delsig.valid",
                 _("Delete this good signature? (y/N/q)"));

	      /* Only update trust if we delete a good signature.
	         The other two cases do not affect trust. */
	      if (okay)
		update_trust = 1;
	    }
	  else if (inv_sig || other_err)
	    okay = cpr_get_answer_yes_no_quit
              ("keyedit.delsig.invalid",
               _("Delete this invalid signature? (y/N/q)"));
	  else if (no_key)
	    okay = cpr_get_answer_yes_no_quit
              ("keyedit.delsig.unknown",
               _("Delete this unknown signature? (y/N/q)"));

	  if (okay == -1)
	    break;
	  if (okay && selfsig
	      && !cpr_get_answer_is_yes
              ("keyedit.delsig.selfsig",
               _("Really delete this self-signature? (y/N)")))
	    okay = 0;
	  if (okay)
	    {
	      delete_kbnode (node);
	      changed++;
	    }

	}
      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	uid = NULL;
    }

  if (changed)
    {
      commit_kbnode (&pub_keyblock);
      tty_printf (ngettext("Deleted %d signature.\n",
                           "Deleted %d signatures.\n", changed), changed);
    }
  else
    tty_printf (_("Nothing deleted.\n"));

  return changed;
}


/* Note: OPTIONS are from the EXPORT_* set. */
static int
menu_clean (ctrl_t ctrl, kbnode_t keyblock, unsigned int options)
{
  KBNODE uidnode;
  int modified = 0;
  int select_all = !count_selected_uids (keyblock);

  for (uidnode = keyblock->next;
       uidnode && uidnode->pkt->pkttype != PKT_PUBLIC_SUBKEY;
       uidnode = uidnode->next)
    {
      if (uidnode->pkt->pkttype == PKT_USER_ID
	  && (uidnode->flag & NODFLG_SELUID || select_all))
	{
	  int uids = 0, sigs = 0;
	  char *user = utf8_to_native (uidnode->pkt->pkt.user_id->name,
				       uidnode->pkt->pkt.user_id->len,
				       0);

	  clean_one_uid (ctrl, keyblock, uidnode, opt.verbose, options,
                         &uids, &sigs);
	  if (uids)
	    {
	      const char *reason;

	      if (uidnode->pkt->pkt.user_id->flags.revoked)
		reason = _("revoked");
	      else if (uidnode->pkt->pkt.user_id->flags.expired)
		reason = _("expired");
	      else
		reason = _("invalid");

	      tty_printf (_("User ID \"%s\" compacted: %s\n"), user, reason);

	      modified = 1;
	    }
	  else if (sigs)
	    {
	      tty_printf (ngettext("User ID \"%s\": %d signature removed\n",
                                   "User ID \"%s\": %d signatures removed\n",
                                   sigs), user, sigs);
	      modified = 1;
	    }
	  else
	    {
	      tty_printf ((options & EXPORT_MINIMAL)?
			  _("User ID \"%s\": already minimized\n") :
			  _("User ID \"%s\": already clean\n"), user);
	    }

	  xfree (user);
	}
    }

  return modified;
}


/*
 * Remove some of the secondary keys
 */
static void
menu_delkey (KBNODE pub_keyblock)
{
  KBNODE node;
  int selected = 0;

  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	{
	  selected = node->flag & NODFLG_SELKEY;
	  if (selected)
            delete_kbnode (node);
	}
      else if (selected && node->pkt->pkttype == PKT_SIGNATURE)
	delete_kbnode (node);
      else
	selected = 0;
    }
  commit_kbnode (&pub_keyblock);

  /* No need to set update_trust here since signing keys are no
     longer used to certify other keys, so there is no change in
     trust when revoking/removing them.   */
}


/*
 * Ask for a new revoker, create the self-signature and put it into
 * the keyblock.  Returns true if there is a new revoker.
 */
static int
menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
{
  PKT_public_key *pk = NULL;
  PKT_public_key *revoker_pk = NULL;
  PKT_signature *sig = NULL;
  PACKET *pkt;
  struct revocation_key revkey;
  size_t fprlen;
  int rc;

  log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);

  pk = pub_keyblock->pkt->pkt.public_key;

  if (pk->numrevkeys == 0 && pk->version == 3)
    {
      /* It is legal but bad for compatibility to add a revoker to a
         v3 key as it means that PGP2 will not be able to use that key
         anymore.  Also, PGP may not expect a revoker on a v3 key.
         Don't bother to ask this if the key already has a revoker -
         any damage has already been done at that point. -dms */
      if (opt.expert)
	{
	  tty_printf (_("WARNING: This is a PGP 2.x-style key.  "
			"Adding a designated revoker may cause\n"
			"         some versions of PGP to reject this key.\n"));

	  if (!cpr_get_answer_is_yes ("keyedit.v3_revoker.okay",
				      _("Are you sure you still want "
					"to add it? (y/N) ")))
	    return 0;
	}
      else
	{
	  tty_printf (_("You may not add a designated revoker to "
			"a PGP 2.x-style key.\n"));
	  return 0;
	}
    }

  for (;;)
    {
      char *answer;

      free_public_key (revoker_pk);
      revoker_pk = xmalloc_clear (sizeof (*revoker_pk));

      tty_printf ("\n");

      answer = cpr_get_utf8
        ("keyedit.add_revoker",
         _("Enter the user ID of the designated revoker: "));
      if (answer[0] == '\0' || answer[0] == CONTROL_D)
	{
	  xfree (answer);
	  goto fail;
	}

      /* Note that I'm requesting CERT here, which usually implies
         primary keys only, but some casual testing shows that PGP and
         GnuPG both can handle a designated revocation from a subkey. */
      revoker_pk->req_usage = PUBKEY_USAGE_CERT;
      rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
                              NULL, revoker_pk, answer, NULL, NULL, 1);
      if (rc)
	{
	  log_error (_("key \"%s\" not found: %s\n"), answer,
		     gpg_strerror (rc));
	  xfree (answer);
	  continue;
	}

      xfree (answer);

      fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen);
      if (fprlen != 20 && fprlen != 32)
	{
	  log_error (_("cannot appoint a PGP 2.x style key as a "
		       "designated revoker\n"));
	  continue;
	}

      revkey.fprlen = fprlen;
      revkey.class = 0x80;
      if (sensitive)
	revkey.class |= 0x40;
      revkey.algid = revoker_pk->pubkey_algo;

      if (cmp_public_keys (revoker_pk, pk) == 0)
	{
	  /* This actually causes no harm (after all, a key that
	     designates itself as a revoker is the same as a
	     regular key), but it's easy enough to check. */
	  log_error (_("you cannot appoint a key as its own "
		       "designated revoker\n"));

	  continue;
	}

      keyid_from_pk (pk, NULL);

      /* Does this revkey already exist? */
      if (!pk->revkey && pk->numrevkeys)
	BUG ();
      else
	{
	  int i;

	  for (i = 0; i < pk->numrevkeys; i++)
	    {
	      if (memcmp (&pk->revkey[i], &revkey,
			  sizeof (struct revocation_key)) == 0)
		{
		  char buf[50];

		  log_error (_("this key has already been designated "
			       "as a revoker\n"));

                  format_keyid (pk_keyid (pk), KF_LONG, buf, sizeof (buf));
		  write_status_text (STATUS_ALREADY_SIGNED, buf);

		  break;
		}
	    }

	  if (i < pk->numrevkeys)
	    continue;
	}

      print_key_info (ctrl, NULL, 0, revoker_pk, 0);
      print_fingerprint (ctrl, NULL, revoker_pk, 2);
      tty_printf ("\n");

      tty_printf (_("WARNING: appointing a key as a designated revoker "
		    "cannot be undone!\n"));

      tty_printf ("\n");

      if (!cpr_get_answer_is_yes ("keyedit.add_revoker.okay",
				  _("Are you sure you want to appoint this "
				    "key as a designated revoker? (y/N) ")))
	continue;

      free_public_key (revoker_pk);
      revoker_pk = NULL;
      break;
    }

  rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, 0x1F, 0, 0,
			   keygen_add_revkey, &revkey, NULL);
  if (rc)
    {
      write_status_error ("keysig", rc);
      log_error ("signing failed: %s\n", gpg_strerror (rc));
      goto fail;
    }

  /* Insert into public keyblock.  */
  pkt = xmalloc_clear (sizeof *pkt);
  pkt->pkttype = PKT_SIGNATURE;
  pkt->pkt.signature = sig;
  insert_kbnode (pub_keyblock, new_kbnode (pkt), PKT_SIGNATURE);

  return 1;

fail:
  if (sig)
    free_seckey_enc (sig);
  free_public_key (revoker_pk);

  return 0;
}


/* Core function to add an ADSK to the KEYBLOCK.  Returns 0 on success
 * or an error code.  If SIGTIMESTAMP is not 0 it is used for the key
 * binding signature creation time; if not given the current time is
 * used.  CACHE_NONCE can be used to avoid a second Pinetry pop-up for
 * appending the ADSK. */
gpg_error_t
append_adsk_to_key (ctrl_t ctrl, kbnode_t keyblock, PKT_public_key *adsk,
                    u32 sigtimestamp, const char *cache_nonce)
{
  gpg_error_t err;
  PKT_public_key *main_pk;  /* The primary key.  */
  PKT_signature *sig = NULL;
  kbnode_t adsknode = NULL;
  PACKET *pkt;  /* (temp. use; will be put into a kbnode_t)  */

  /* First get a copy.  */
  adsk = copy_public_key_basics (NULL, adsk);

  /* Check compliance.  */
  if (!gnupg_pk_is_compliant (opt.compliance, adsk->pubkey_algo, 0,
                              adsk->pkey, nbits_from_pk (adsk), NULL))
    {
      char pkhex[MAX_FINGERPRINT_LEN*2+1];

      hexfingerprint (adsk, pkhex, sizeof pkhex);
      log_error (_("WARNING: key %s is not suitable for encryption"
                   " in %s mode\n"),
                 pkhex, gnupg_compliance_option_string (opt.compliance));
      err = gpg_error (GPG_ERR_FORBIDDEN);
      goto leave;
    }

  /* Get the primary key.  */
  log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
  main_pk = keyblock->pkt->pkt.public_key;

  /* Prepare and append the adsk.  */
  keyid_from_pk (main_pk, adsk->main_keyid); /* Fixup main keyid.  */
  log_assert ((adsk->pubkey_usage & PUBKEY_USAGE_XENC_MASK));
  adsk->pubkey_usage = PUBKEY_USAGE_RENC;    /* 'e' or 'r' -> 'r'  */
  pkt = xtrycalloc (1, sizeof *pkt);
  if (!pkt)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  pkt->pkttype = PKT_PUBLIC_SUBKEY;    /* Make sure it is a subkey.  */
  pkt->pkt.public_key = adsk;
  adsknode = new_kbnode (pkt);

  /* Make the signature.  */
  err = make_keysig_packet (ctrl, &sig, main_pk, NULL, adsk, main_pk, 0x18,
                            sigtimestamp, 0,
                            keygen_add_key_flags_and_expire, adsk, cache_nonce);
  adsk = NULL; /* (owned by adsknode - avoid double free.)  */
  if (err)
    {
      write_status_error ("keysig", err);
      log_error ("creating key binding failed: %s\n", gpg_strerror (err));
      goto leave;
    }

  /* Append the subkey packet and the binding signature.  */
  add_kbnode (keyblock, adsknode);
  adsknode = NULL;
  pkt = xtrycalloc (1, sizeof *pkt);
  if (!pkt)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  pkt->pkttype = PKT_SIGNATURE;
  pkt->pkt.signature = sig;
  add_kbnode (keyblock, new_kbnode (pkt));

 leave:
  release_kbnode (adsknode);
  free_public_key (adsk);  /* Release our copy.  */
  return err;
}


/*
 * Ask for a new additional decryption subkey and add it to the key
 * block.  Returns true if the keyblock was changed and false
 * otherwise.  If ADSKFPR is not NULL, this function has been called
 * by quick_addadsk and gives the fingerprint of the to be added key.
 */
static int
menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock, const char *adskfpr)
{
  PKT_public_key *pk;
  PKT_public_key *adsk_pk = NULL;
  kbnode_t adsk_keyblock = NULL;
  char *answer = NULL;
  gpg_error_t err;
  KEYDB_SEARCH_DESC desc;
  byte fpr[MAX_FINGERPRINT_LEN];
  size_t fprlen;
  kbnode_t node;
  u32 sigtimestamp = make_timestamp ();

  log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);

  for (;;)
    {
      xfree (answer);
      if (adskfpr)
        answer = xstrdup (adskfpr);
      else
        {
          answer = cpr_get_utf8
            ("keyedit.addadsk",
             _("Enter the fingerprint of the additional decryption subkey: "));
          if (answer[0] == '\0' || answer[0] == CONTROL_D)
            {
              err = gpg_error (GPG_ERR_CANCELED);
              goto leave;
            }
        }
      if (classify_user_id (answer, &desc, 1)
          || desc.mode != KEYDB_SEARCH_MODE_FPR)
        {
          log_info (_("\"%s\" is not a fingerprint\n"), answer);
          err = gpg_error (GPG_ERR_INV_USER_ID);
          if (adskfpr)
            goto leave;
          continue;
        }

      /* Force searching for that exact fingerprint and for any key
       * which has a key with that fingerprint.  */
      if (!strchr (answer, '!'))
        {
          char *tmpstr = xstrconcat (answer, "!", NULL);
          xfree (answer);
          answer = tmpstr;
        }

      free_public_key (adsk_pk);
      adsk_pk = xcalloc (1, sizeof *adsk_pk);
      adsk_pk->req_usage = PUBKEY_USAGE_ENC;
      release_kbnode (adsk_keyblock);
      adsk_keyblock = NULL;
      err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
                               NULL, adsk_pk, answer, &adsk_keyblock, NULL, 1);
      if (err)
        {
          write_status_error ("add_adsk", err);
          log_info (_("key \"%s\" not found: %s\n"), answer,
                    gpg_strerror (err));
          if ((!opt.batch || adskfpr) && !opt.quiet
              && gpg_err_code (err) == GPG_ERR_UNUSABLE_PUBKEY)
            log_info (_("Did you specify the fingerprint of a subkey?\n"));
          if (adskfpr)
            goto leave;
	  continue;
	}

      for (node = adsk_keyblock; node; node = node->next)
        {
          if (node->pkt->pkttype == PKT_PUBLIC_KEY
              || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
            {
              pk = node->pkt->pkt.public_key;
              fingerprint_from_pk (pk, fpr, &fprlen);
              if (fprlen == desc.fprlen
                  && !memcmp (fpr, desc.u.fpr, fprlen)
                  && (pk->pubkey_usage & PUBKEY_USAGE_ENC))
                break;
            }
        }
      if (!node)
        {
          write_status_error ("add_adsk", err);
          err = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
          log_info (_("key \"%s\" not found: %s\n"), answer,
                    gpg_strerror (err));
          if ((!opt.batch || adskfpr) && !opt.quiet)
            log_info (_("Did you specify the fingerprint of a subkey?\n"));
          if (adskfpr)
            goto leave;
	  continue;
        }

      /* Check that the selected subkey is not yet on our keyblock.  */
      err = has_key_with_fingerprint (pub_keyblock, desc.u.fpr, desc.fprlen);
      if (err)
        {
          log_info (_("key \"%s\" is already on this keyblock\n"), answer);
          if (adskfpr)
            goto leave;
          continue;
        }

      break;
    }

  /* Append the subkey.  */
  log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY
              || node->pkt->pkttype == PKT_PUBLIC_SUBKEY);
  err = append_adsk_to_key (ctrl, pub_keyblock, node->pkt->pkt.public_key,
                            sigtimestamp, NULL);


 leave:
  xfree (answer);
  free_public_key (adsk_pk);
  release_kbnode (adsk_keyblock);
  if (!err)
    return 1; /* The keyblock was modified.  */
  else
    return 0; /* Not modified.  */

}


/* With FORCE_MAINKEY cleared this function handles the interactive
 * menu option "expire".  With UNATTENDED set to 1 this function only
 * sets the expiration date of the primary key to NEWEXPIRATION and
 * avoid all interactivity; with a value of 2 only the flagged subkeys
 * are set to NEWEXPIRATION.  Returns 0 if nothing was done,
 * GPG_ERR_TRUE if the key was modified, or any other error code. */
static gpg_error_t
menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock,
             int unattended, u32 newexpiration)
{
  int signumber, rc;
  u32 expiredate;
  int only_mainkey;  /* Set if only the mainkey is to be updated.  */
  PKT_public_key *main_pk, *sub_pk;
  PKT_user_id *uid;
  kbnode_t node;
  u32 keyid[2];

  (void)signumber;
  if (unattended)
    {
      only_mainkey = (unattended == 1);
      expiredate = newexpiration;
    }
  else
    {
      int n1;

      only_mainkey = 0;
      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"));
          only_mainkey = 1;
          no_primary_warning (pub_keyblock);
        }

      expiredate = ask_expiredate ();
    }


  /* Now we can actually change the self-signature(s) */
  main_pk = sub_pk = NULL;
  uid = NULL;
  signumber = 0;
  for (node = pub_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);
	  main_pk->expiredate = expiredate;
	}
      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	{
          if ((node->flag & NODFLG_SELKEY) && unattended != 1)
            {
              /* The flag is set and we do not want to set the
               * expiration date only for the main key.  */
              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 (main_pk && node->pkt->pkttype == PKT_SIGNATURE
	       && (only_mainkey || sub_pk))
	{
	  PKT_signature *sig = node->pkt->pkt.signature;

	  if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
	      && ((only_mainkey && uid
		   && uid->created && (sig->sig_class & ~3) == 0x10)
		  || (!only_mainkey && sig->sig_class == 0x18))
	      && sig->flags.chosen_selfsig)
	    {
	      /* This is a self-signature which is to be replaced.  */
	      PKT_signature *newsig;
	      PACKET *newpkt;

	      signumber++;

	      if ((only_mainkey && main_pk->version < 4)
		  || (!only_mainkey && sub_pk->version < 4))
		{
		  log_info
                    (_("You can't change the expiration date of a v3 key\n"));
		  return gpg_error (GPG_ERR_LEGACY_KEY);
		}

	      if (only_mainkey)
		rc = update_keysig_packet (ctrl,
                                           &newsig, sig, main_pk, uid, NULL,
					   main_pk, keygen_add_key_expire,
					   main_pk);
	      else
		rc =
		  update_keysig_packet (ctrl,
                                        &newsig, sig, main_pk, NULL, sub_pk,
					main_pk, keygen_add_key_expire, sub_pk);
	      if (rc)
		{
		  log_error ("make_keysig_packet failed: %s\n",
			     gpg_strerror (rc));
                  if (gpg_err_code (rc) == GPG_ERR_TRUE)
                    rc = GPG_ERR_GENERAL;
		  return rc;
		}

	      /* Replace the packet.  */
	      newpkt = xmalloc_clear (sizeof *newpkt);
	      newpkt->pkttype = PKT_SIGNATURE;
	      newpkt->pkt.signature = newsig;
	      free_packet (node->pkt, NULL);
	      xfree (node->pkt);
	      node->pkt = newpkt;
	      sub_pk = NULL;
	    }
	}
    }

  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 (ctrl_t ctrl, 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))
		{
                  /* Note: This won't happen because we don't support
                   * v3 keys anymore.  */
		  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 (ctrl,
                                           &newsig, sig, main_pk, uid, NULL,
					   main_pk, keygen_add_key_flags,
					   main_pk);
	      else
		rc =
		  update_keysig_packet (ctrl,
                                        &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, NULL);
	      xfree (node->pkt);
	      node->pkt = newpkt;
	      sub_pk = NULL;
              break;
	    }
	}
    }

  return 1;
}


static int
menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock)
{
  int rc, modified = 0;
  PKT_public_key *main_pk;
  KBNODE node;
  u32 timestamp;

  log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);

  merge_keys_and_selfsig (ctrl, pub_keyblock);
  main_pk = pub_keyblock->pkt->pkt.public_key;
  keyid_from_pk (main_pk, NULL);

  /* We use the same timestamp for all backsigs so that we don't
     reveal information about the used machine.  */
  timestamp = make_timestamp ();

  for (node = pub_keyblock; node; node = node->next)
    {
      PKT_public_key *sub_pk = NULL;
      KBNODE node2, sig_pk = NULL /*,sig_sk = NULL*/;
      /* char *passphrase; */

      /* Find a signing subkey with no backsig */
      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	{
	  if (node->pkt->pkt.public_key->pubkey_usage & PUBKEY_USAGE_SIG)
	    {
	      if (node->pkt->pkt.public_key->flags.backsig)
		tty_printf (_
			    ("signing subkey %s is already cross-certified\n"),
			    keystr_from_pk (node->pkt->pkt.public_key));
	      else
		sub_pk = node->pkt->pkt.public_key;
	    }
	  else
	    tty_printf (_("subkey %s does not sign and so does"
			  " not need to be cross-certified\n"),
			keystr_from_pk (node->pkt->pkt.public_key));
	}

      if (!sub_pk)
	continue;

      /* Find the selected selfsig on this subkey */
      for (node2 = node->next;
	   node2 && node2->pkt->pkttype == PKT_SIGNATURE; node2 = node2->next)
	if (node2->pkt->pkt.signature->version >= 4
	    && node2->pkt->pkt.signature->flags.chosen_selfsig)
	  {
	    sig_pk = node2;
	    break;
	  }

      if (!sig_pk)
	continue;

      /* Find the secret subkey that matches the public subkey */
      log_debug ("FIXME: Check whether a secret subkey is available.\n");
      /* if (!sub_sk) */
      /*   { */
      /*     tty_printf (_("no secret subkey for public subkey %s - ignoring\n"), */
      /*   	      keystr_from_pk (sub_pk)); */
      /*     continue; */
      /*   } */


      /* Now we can get to work.  */

      rc = make_backsig (ctrl,
                         sig_pk->pkt->pkt.signature, main_pk, sub_pk, sub_pk,
			 timestamp, NULL);
      if (!rc)
	{
	  PKT_signature *newsig;
	  PACKET *newpkt;

	  rc = update_keysig_packet (ctrl,
                                     &newsig, sig_pk->pkt->pkt.signature,
                                     main_pk, NULL, sub_pk, main_pk,
                                     NULL, NULL);
	  if (!rc)
	    {
	      /* Put the new sig into place on the pubkey */
	      newpkt = xmalloc_clear (sizeof (*newpkt));
	      newpkt->pkttype = PKT_SIGNATURE;
	      newpkt->pkt.signature = newsig;
	      free_packet (sig_pk->pkt, NULL);
	      xfree (sig_pk->pkt);
	      sig_pk->pkt = newpkt;

	      modified = 1;
	    }
	  else
	    {
	      log_error ("update_keysig_packet failed: %s\n",
			 gpg_strerror (rc));
	      break;
	    }
	}
      else
	{
	  log_error ("make_backsig failed: %s\n", gpg_strerror (rc));
	  break;
	}
    }

  return modified;
}


static int
change_primary_uid_cb (PKT_signature * sig, void *opaque)
{
  byte buf[1];

  /* first clear all primary uid flags so that we are sure none are
   * lingering around */
  delete_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID);
  delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID);

  /* if opaque is set,we want to set the primary id */
  if (opaque)
    {
      buf[0] = 1;
      build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, buf, 1);
    }

  return 0;
}


/*
 * Set the primary uid flag for the selected UID.  We will also reset
 * 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
 * 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.
 */
static int
menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock)
{
  PKT_public_key *main_pk;
  PKT_user_id *uid;
  KBNODE node;
  u32 keyid[2];
  int selected;
  int attribute = 0;
  int modified = 0;

  if (count_selected_uids (pub_keyblock) != 1)
    {
      tty_printf (_("Please select exactly one user ID.\n"));
      return 0;
    }

  main_pk = NULL;
  uid = NULL;
  selected = 0;

  /* Is our selected uid an attribute packet? */
  for (node = pub_keyblock; node; node = node->next)
    if (node->pkt->pkttype == PKT_USER_ID && node->flag & NODFLG_SELUID)
      attribute = (node->pkt->pkt.user_id->attrib_data != NULL);

  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	break; /* No more user ids expected - ready.  */

      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_USER_ID)
	{
	  uid = node->pkt->pkt.user_id;
	  selected = node->flag & NODFLG_SELUID;
	}
      else if (main_pk && uid && node->pkt->pkttype == PKT_SIGNATURE)
	{
	  PKT_signature *sig = node->pkt->pkt.signature;
	  if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
	      && (uid && (sig->sig_class & ~3) == 0x10)
	      && attribute == (uid->attrib_data != NULL)
	      && sig->flags.chosen_selfsig)
	    {
	      if (sig->version < 4)
		{
		  char *user =
		    utf8_to_native (uid->name, strlen (uid->name), 0);

		  log_info (_("skipping v3 self-signature on user ID \"%s\"\n"),
                            user);
		  xfree (user);
		}
	      else
		{
		  /* This is a selfsignature which is to be replaced.
		     We can just ignore v3 signatures because they are
		     not able to carry the primary ID flag.  We also
		     ignore self-sigs on user IDs that are not of the
		     same type that we are making primary.  That is, if
		     we are making a user ID primary, we alter user IDs.
		     If we are making an attribute packet primary, we
		     alter attribute packets. */

		  /* FIXME: We must make sure that we only have one
		     self-signature per user ID here (not counting
		     revocations) */
		  PKT_signature *newsig;
		  PACKET *newpkt;
		  const byte *p;
		  int action;

		  /* See whether this signature has the primary UID flag.  */
		  p = parse_sig_subpkt (sig, 1,
					SIGSUBPKT_PRIMARY_UID, NULL);
		  if (!p)
		    p = parse_sig_subpkt (sig, 0,
					  SIGSUBPKT_PRIMARY_UID, NULL);
		  if (p && *p)	/* yes */
		    action = selected ? 0 : -1;
		  else		/* no */
		    action = selected ? 1 : 0;

		  if (action)
		    {
		      int rc = update_keysig_packet (ctrl, &newsig, sig,
						     main_pk, uid, NULL,
						     main_pk,
						     change_primary_uid_cb,
						     action > 0 ? "x" : NULL);
		      if (rc)
			{
			  log_error ("update_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, NULL);
		      xfree (node->pkt);
		      node->pkt = newpkt;
		      modified = 1;
		    }
		}
	    }
	}
    }

  return modified;
}


/*
 * Set preferences to new values for the selected user IDs.
 * --quick-update-pred calls this with UNATTENDED set.
 */
static int
menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended)
{
  PKT_public_key *main_pk;
  PKT_user_id *uid;
  KBNODE node;
  u32 keyid[2];
  int selected, select_all;
  int modified = 0;

  if (!unattended)
    no_primary_warning (pub_keyblock);

  select_all = unattended? 1 : !count_selected_uids (pub_keyblock);

  /* Now we can actually change the self signature(s) */
  main_pk = NULL;
  uid = NULL;
  selected = 0;
  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	break; /* No more user-ids expected - ready.  */

      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_USER_ID)
	{
	  uid = node->pkt->pkt.user_id;
	  selected = select_all || (node->flag & NODFLG_SELUID);
	}
      else if (main_pk && uid && selected
	       && node->pkt->pkttype == PKT_SIGNATURE)
	{
	  PKT_signature *sig = node->pkt->pkt.signature;
	  if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
	      && (uid && (sig->sig_class & ~3) == 0x10)
	      && sig->flags.chosen_selfsig)
	    {
	      if (sig->version < 4)
		{
		  char *user =
		    utf8_to_native (uid->name, strlen (uid->name), 0);

		  log_info (_("skipping v3 self-signature on user ID \"%s\"\n"),
			    user);
		  xfree (user);
		}
	      else
		{
		  /* This is a selfsignature which is to be replaced
		   * We have to ignore v3 signatures because they are
		   * not able to carry the preferences.  */
		  PKT_signature *newsig;
		  PACKET *newpkt;
		  int rc;

		  rc = update_keysig_packet (ctrl, &newsig, sig,
					     main_pk, uid, NULL, main_pk,
                                             keygen_upd_std_prefs, NULL);
		  if (rc)
		    {
		      log_error ("update_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, NULL);
		  xfree (node->pkt);
		  node->pkt = newpkt;
		  modified = 1;
		}
	    }
	}
    }

  return modified;
}


static int
menu_set_keyserver_url (ctrl_t ctrl, const char *url, kbnode_t pub_keyblock)
{
  PKT_public_key *main_pk;
  PKT_user_id *uid;
  KBNODE node;
  u32 keyid[2];
  int selected, select_all;
  int modified = 0;
  char *answer, *uri;

  no_primary_warning (pub_keyblock);

  if (url)
    answer = xstrdup (url);
  else
    {
      answer = cpr_get_utf8 ("keyedit.add_keyserver",
			     _("Enter your preferred keyserver URL: "));
      if (answer[0] == '\0' || answer[0] == CONTROL_D)
	{
	  xfree (answer);
	  return 0;
	}
    }

  if (!ascii_strcasecmp (answer, "none"))
    {
      xfree (answer);
      uri = NULL;
    }
  else
    {
      struct keyserver_spec *keyserver = NULL;
      /* Sanity check the format */
      keyserver = parse_keyserver_uri (answer, 1);
      xfree (answer);
      if (!keyserver)
	{
	  log_info (_("could not parse keyserver URL\n"));
	  return 0;
	}
      uri = xstrdup (keyserver->uri);
      free_keyserver_spec (keyserver);
    }

  select_all = !count_selected_uids (pub_keyblock);

  /* Now we can actually change the self signature(s) */
  main_pk = NULL;
  uid = NULL;
  selected = 0;
  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	break; /* ready */

      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_USER_ID)
	{
	  uid = node->pkt->pkt.user_id;
	  selected = select_all || (node->flag & NODFLG_SELUID);
	}
      else if (main_pk && uid && selected
	       && node->pkt->pkttype == PKT_SIGNATURE)
	{
	  PKT_signature *sig = node->pkt->pkt.signature;
	  if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
	      && (uid && (sig->sig_class & ~3) == 0x10)
	      && sig->flags.chosen_selfsig)
	    {
	      char *user = utf8_to_native (uid->name, strlen (uid->name), 0);
	      if (sig->version < 4)
		log_info (_("skipping v3 self-signature on user ID \"%s\"\n"),
			  user);
	      else
		{
		  /* This is a selfsignature which is to be replaced
		   * We have to ignore v3 signatures because they are
		   * not able to carry the subpacket. */
		  PKT_signature *newsig;
		  PACKET *newpkt;
		  int rc;
		  const byte *p;
		  size_t plen;

		  p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, &plen);
		  if (p && plen)
		    {
		      tty_printf ("Current preferred keyserver for user"
				  " ID \"%s\": ", user);
		      tty_print_utf8_string (p, plen);
		      tty_printf ("\n");
		      if (!cpr_get_answer_is_yes
                          ("keyedit.confirm_keyserver",
                           uri
                           ? _("Are you sure you want to replace it? (y/N) ")
                           : _("Are you sure you want to delete it? (y/N) ")))
		        {
			  xfree (user);
			  continue;
		        }
		    }
		  else if (uri == NULL)
		    {
		      /* There is no current keyserver URL, so there
		         is no point in trying to un-set it. */
                      xfree (user);
		      continue;
		    }

		  rc = update_keysig_packet (ctrl, &newsig, sig,
					     main_pk, uid, NULL,
					     main_pk,
					     keygen_add_keyserver_url, uri);
		  if (rc)
		    {
		      log_error ("update_keysig_packet failed: %s\n",
				 gpg_strerror (rc));
		      xfree (uri);
	              xfree (user);
		      return 0;
		    }
		  /* replace the packet */
		  newpkt = xmalloc_clear (sizeof *newpkt);
		  newpkt->pkttype = PKT_SIGNATURE;
		  newpkt->pkt.signature = newsig;
		  free_packet (node->pkt, NULL);
		  xfree (node->pkt);
		  node->pkt = newpkt;
		  modified = 1;
		}

	      xfree (user);
	    }
	}
    }

  xfree (uri);
  return modified;
}


static int
menu_set_notation (ctrl_t ctrl, const char *string, KBNODE pub_keyblock)
{
  PKT_public_key *main_pk;
  PKT_user_id *uid;
  KBNODE node;
  u32 keyid[2];
  int selected, select_all;
  int modified = 0;
  char *answer;
  struct notation *notation;

  no_primary_warning (pub_keyblock);

  if (string)
    answer = xstrdup (string);
  else
    {
      answer = cpr_get_utf8 ("keyedit.add_notation",
			     _("Enter the notation: "));
      if (answer[0] == '\0' || answer[0] == CONTROL_D)
	{
	  xfree (answer);
	  return 0;
	}
    }

  if (!ascii_strcasecmp (answer, "none")
      || !ascii_strcasecmp (answer, "-"))
    notation = NULL; /* Delete them all.  */
  else
    {
      notation = string_to_notation (answer, 0);
      if (!notation)
	{
	  xfree (answer);
	  return 0;
	}
    }

  xfree (answer);

  select_all = !count_selected_uids (pub_keyblock);

  /* Now we can actually change the self signature(s) */
  main_pk = NULL;
  uid = NULL;
  selected = 0;
  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
	break; /* ready */

      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_USER_ID)
	{
	  uid = node->pkt->pkt.user_id;
	  selected = select_all || (node->flag & NODFLG_SELUID);
	}
      else if (main_pk && uid && selected
	       && node->pkt->pkttype == PKT_SIGNATURE)
	{
	  PKT_signature *sig = node->pkt->pkt.signature;
	  if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
	      && (uid && (sig->sig_class & ~3) == 0x10)
	      && sig->flags.chosen_selfsig)
	    {
	      char *user = utf8_to_native (uid->name, strlen (uid->name), 0);
	      if (sig->version < 4)
		log_info (_("skipping v3 self-signature on user ID \"%s\"\n"),
			  user);
	      else
		{
		  PKT_signature *newsig;
		  PACKET *newpkt;
		  int rc, skip = 0, addonly = 1;

		  if (sig->flags.notation)
		    {
		      tty_printf ("Current notations for user ID \"%s\":\n",
				  user);
		      tty_print_notations (-9, sig);
		    }
		  else
		    {
		      tty_printf ("No notations on user ID \"%s\"\n", user);
		      if (notation == NULL)
			{
			  /* There are no current notations, so there
			     is no point in trying to un-set them. */
			  continue;
			}
		    }

		  if (notation)
		    {
		      struct notation *n;
		      int deleting = 0;

		      notation->next = sig_to_notation (sig);

		      for (n = notation->next; n; n = n->next)
			if (strcmp (n->name, notation->name) == 0)
			  {
			    if (notation->value)
			      {
				if (strcmp (n->value, notation->value) == 0)
				  {
				    if (notation->flags.ignore)
				      {
					/* Value match with a delete
					   flag. */
					n->flags.ignore = 1;
					deleting = 1;
				      }
				    else
				      {
					/* Adding the same notation
					   twice, so don't add it at
					   all. */
					skip = 1;
					tty_printf ("Skipping notation:"
						    " %s=%s\n",
						    notation->name,
						    notation->value);
					break;
				      }
				  }
			      }
			    else
			      {
				/* No value, so it means delete. */
				n->flags.ignore = 1;
				deleting = 1;
			      }

			    if (n->flags.ignore)
			      {
				tty_printf ("Removing notation: %s=%s\n",
					    n->name, n->value);
				addonly = 0;
			      }
			  }

		      if (!notation->flags.ignore && !skip)
			tty_printf ("Adding notation: %s=%s\n",
				    notation->name, notation->value);

		      /* We tried to delete, but had no matches.  */
		      if (notation->flags.ignore && !deleting)
			continue;
		    }
		  else
		    {
		      tty_printf ("Removing all notations\n");
		      addonly = 0;
		    }

		  if (skip
		      || (!addonly
			  &&
			  !cpr_get_answer_is_yes ("keyedit.confirm_notation",
						  _("Proceed? (y/N) "))))
		    continue;

		  rc = update_keysig_packet (ctrl, &newsig, sig,
					     main_pk, uid, NULL,
					     main_pk,
					     keygen_add_notations, notation);
		  if (rc)
		    {
		      log_error ("update_keysig_packet failed: %s\n",
				 gpg_strerror (rc));
		      free_notation (notation);
		      xfree (user);
		      return 0;
		    }

		  /* replace the packet */
		  newpkt = xmalloc_clear (sizeof *newpkt);
		  newpkt->pkttype = PKT_SIGNATURE;
		  newpkt->pkt.signature = newsig;
		  free_packet (node->pkt, NULL);
		  xfree (node->pkt);
		  node->pkt = newpkt;
		  modified = 1;

		  if (notation)
		    {
		      /* Snip off the notation list from the sig */
		      free_notation (notation->next);
		      notation->next = NULL;
		    }

		  xfree (user);
		}
	    }
	}
    }

  free_notation (notation);
  return modified;
}


/*
 * Select one user id or remove all selection if IDX is 0 or select
 * all if IDX is -1.  Returns: True if the selection changed.
 */
static int
menu_select_uid (KBNODE keyblock, int idx)
{
  KBNODE node;
  int i;

  if (idx == -1)		/* Select all. */
    {
      for (node = keyblock; node; node = node->next)
	if (node->pkt->pkttype == PKT_USER_ID)
	  node->flag |= NODFLG_SELUID;
      return 1;
    }
  else if (idx)			/* Toggle.  */
    {
      for (i = 0, node = keyblock; node; node = node->next)
	{
	  if (node->pkt->pkttype == PKT_USER_ID)
	    if (++i == idx)
	      break;
	}
      if (!node)
	{
	  tty_printf (_("No user ID with index %d\n"), idx);
	  return 0;
	}

      for (i = 0, node = keyblock; node; node = node->next)
	{
	  if (node->pkt->pkttype == PKT_USER_ID)
	    {
	      if (++i == idx)
		{
		  if ((node->flag & NODFLG_SELUID))
		    node->flag &= ~NODFLG_SELUID;
		  else
		    node->flag |= NODFLG_SELUID;
		}
	    }
	}
    }
  else				/* Unselect all */
    {
      for (node = keyblock; node; node = node->next)
	if (node->pkt->pkttype == PKT_USER_ID)
	  node->flag &= ~NODFLG_SELUID;
    }

  return 1;
}


/* Search in the keyblock for a uid that matches namehash */
static int
menu_select_uid_namehash (KBNODE keyblock, const char *namehash)
{
  byte hash[NAMEHASH_LEN];
  KBNODE node;
  int i;

  log_assert (strlen (namehash) == NAMEHASH_LEN * 2);

  for (i = 0; i < NAMEHASH_LEN; i++)
    hash[i] = hextobyte (&namehash[i * 2]);

  for (node = keyblock->next; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  namehash_from_uid (node->pkt->pkt.user_id);
	  if (memcmp (node->pkt->pkt.user_id->namehash, hash, NAMEHASH_LEN) ==
	      0)
	    {
	      if (node->flag & NODFLG_SELUID)
		node->flag &= ~NODFLG_SELUID;
	      else
		node->flag |= NODFLG_SELUID;

	      break;
	    }
	}
    }

  if (!node)
    {
      tty_printf (_("No user ID with hash %s\n"), namehash);
      return 0;
    }

  return 1;
}


/*
 * Select secondary keys
 * Returns: True if the selection changed.
 */
static int
menu_select_key (KBNODE keyblock, int idx, char *p)
{
  KBNODE node;
  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.  */
    {
      for (node = keyblock; node; node = node->next)
	if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
	    || node->pkt->pkttype == PKT_SECRET_SUBKEY)
	  node->flag |= NODFLG_SELKEY;
    }
  else if (idx)			/* Toggle selection.  */
    {
      for (i = 0, node = keyblock; node; node = node->next)
	{
	  if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
	      || node->pkt->pkttype == PKT_SECRET_SUBKEY)
	    if (++i == idx)
	      break;
	}
      if (!node)
	{
	  tty_printf (_("No subkey with index %d\n"), idx);
	  return 0;
	}

      for (i = 0, node = keyblock; node; node = node->next)
	{
	  if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
	      || node->pkt->pkttype == PKT_SECRET_SUBKEY)
	    if (++i == idx)
	      {
		if ((node->flag & NODFLG_SELKEY))
		  node->flag &= ~NODFLG_SELKEY;
		else
		  node->flag |= NODFLG_SELKEY;
	      }
	}
    }
  else				/* Unselect all. */
    {
      for (node = keyblock; node; node = node->next)
	if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
	    || node->pkt->pkttype == PKT_SECRET_SUBKEY)
	  node->flag &= ~NODFLG_SELKEY;
    }

  return 1;
}


static int
count_uids_with_flag (KBNODE keyblock, unsigned flag)
{
  KBNODE node;
  int i = 0;

  for (node = keyblock; node; node = node->next)
    if (node->pkt->pkttype == PKT_USER_ID && (node->flag & flag))
      i++;
  return i;
}


static int
count_keys_with_flag (KBNODE keyblock, unsigned flag)
{
  KBNODE node;
  int i = 0;

  for (node = keyblock; node; node = node->next)
    if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY
	 || node->pkt->pkttype == PKT_SECRET_SUBKEY) && (node->flag & flag))
      i++;
  return i;
}


static int
count_uids (KBNODE keyblock)
{
  KBNODE node;
  int i = 0;

  for (node = keyblock; node; node = node->next)
    if (node->pkt->pkttype == PKT_USER_ID)
      i++;
  return i;
}


/*
 * Returns true if there is at least one selected user id
 */
static int
count_selected_uids (KBNODE keyblock)
{
  return count_uids_with_flag (keyblock, NODFLG_SELUID);
}


static int
count_selected_keys (KBNODE keyblock)
{
  return count_keys_with_flag (keyblock, NODFLG_SELKEY);
}


/* Returns how many real (i.e. not attribute) uids are unmarked.  */
static int
real_uids_left (KBNODE keyblock)
{
  KBNODE node;
  int real = 0;

  for (node = keyblock; node; node = node->next)
    if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & NODFLG_SELUID) &&
	!node->pkt->pkt.user_id->attrib_data)
      real++;

  return real;
}


/*
 * Ask whether the signature should be revoked.  If the user commits this,
 * flag bit MARK_A is set on the signature and the user ID.
 */
static void
ask_revoke_sig (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node)
{
  int doit = 0;
  PKT_user_id *uid;
  PKT_signature *sig = node->pkt->pkt.signature;
  KBNODE unode = find_prev_kbnode (keyblock, node, PKT_USER_ID);

  if (!unode)
    {
      log_error ("Oops: no user ID for signature\n");
      return;
    }

  uid = unode->pkt->pkt.user_id;

  if (opt.with_colons)
    {
      if (uid->attrib_data)
	printf ("uat:::::::::%u %lu", uid->numattribs, uid->attrib_len);
      else
	{
	  es_printf ("uid:::::::::");
	  es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
	}

      es_printf ("\n");

      print_and_check_one_sig_colon (ctrl, keyblock, node,
                                     NULL, NULL, NULL, NULL, 1);
    }
  else
    {
      char *p = utf8_to_native (unode->pkt->pkt.user_id->name,
				unode->pkt->pkt.user_id->len, 0);
      tty_printf (_("user ID: \"%s\"\n"), p);
      xfree (p);

      tty_printf (_("signed by your key %s on %s%s%s\n"),
		  keystr (sig->keyid), datestr_from_sig (sig),
		  sig->flags.exportable ? "" : _(" (non-exportable)"), "");
    }
  if (sig->flags.expired)
    {
      tty_printf (_("This signature expired on %s.\n"),
		  expirestr_from_sig (sig));
      /* Use a different question so we can have different help text */
      doit = cpr_get_answer_is_yes
        ("ask_revoke_sig.expired",
         _("Are you sure you still want to revoke it? (y/N) "));
    }
  else
    doit = cpr_get_answer_is_yes
      ("ask_revoke_sig.one",
       _("Create a revocation certificate for this signature? (y/N) "));

  if (doit)
    {
      node->flag |= NODFLG_MARK_A;
      unode->flag |= NODFLG_MARK_A;
    }
}


/*
 * Display all user ids of the current public key together with signatures
 * done by one of our keys.  Then walk over all this sigs and ask the user
 * whether he wants to revoke this signature.
 * Return: True when the keyblock has changed.
 */
static int
menu_revsig (ctrl_t ctrl, kbnode_t keyblock)
{
  PKT_signature *sig;
  PKT_public_key *primary_pk;
  KBNODE node;
  int changed = 0;
  int rc, any, skip = 1, all = !count_selected_uids (keyblock);
  struct revocation_reason_info *reason = NULL;

  log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);

  /* First check whether we have any signatures at all.  */
  any = 0;
  for (node = keyblock; node; node = node->next)
    {
      node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A);
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  if (node->flag & NODFLG_SELUID || all)
	    skip = 0;
	  else
	    skip = 1;
	}
      else if (!skip && node->pkt->pkttype == PKT_SIGNATURE
	       && ((sig = node->pkt->pkt.signature),
		   have_secret_key_with_kid (ctrl, sig->keyid)))
	{
	  if ((sig->sig_class & ~3) == 0x10)
	    {
	      any = 1;
	      break;
	    }
	}
    }

  if (!any)
    {
      tty_printf (_("Not signed by you.\n"));
      return 0;
    }


  /* FIXME: detect duplicates here  */
  tty_printf (_("You have signed these user IDs on key %s:\n"),
	      keystr_from_pk (keyblock->pkt->pkt.public_key));
  for (node = keyblock; node; node = node->next)
    {
      node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A);
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  if (node->flag & NODFLG_SELUID || all)
	    {
	      PKT_user_id *uid = node->pkt->pkt.user_id;
	      /* Hmmm: Should we show only UIDs with a signature? */
	      tty_printf ("     ");
	      tty_print_utf8_string (uid->name, uid->len);
	      tty_printf ("\n");
	      skip = 0;
	    }
	  else
	    skip = 1;
	}
      else if (!skip && node->pkt->pkttype == PKT_SIGNATURE
	       && ((sig = node->pkt->pkt.signature),
		   have_secret_key_with_kid (ctrl, sig->keyid)))
	{
	  if ((sig->sig_class & ~3) == 0x10)
	    {
	      tty_printf ("   ");
	      tty_printf (_("signed by your key %s on %s%s%s\n"),
			  keystr (sig->keyid), datestr_from_sig (sig),
			  sig->flags.exportable ? "" : _(" (non-exportable)"),
			  sig->flags.revocable ? "" : _(" (non-revocable)"));
	      if (sig->flags.revocable)
		node->flag |= NODFLG_SELSIG;
	    }
	  else if (sig->sig_class == 0x30)
	    {
	      tty_printf ("   ");
	      tty_printf (_("revoked by your key %s on %s\n"),
			  keystr (sig->keyid), datestr_from_sig (sig));
	    }
	}
    }

  tty_printf ("\n");

  /* ask */
  for (node = keyblock; node; node = node->next)
    {
      if (!(node->flag & NODFLG_SELSIG))
	continue;
      ask_revoke_sig (ctrl, keyblock, node);
    }

  /* present selected */
  any = 0;
  for (node = keyblock; node; node = node->next)
    {
      if (!(node->flag & NODFLG_MARK_A))
	continue;
      if (!any)
	{
	  any = 1;
	  tty_printf (_("You are about to revoke these signatures:\n"));
	}
      if (node->pkt->pkttype == PKT_USER_ID)
	{
	  PKT_user_id *uid = node->pkt->pkt.user_id;
	  tty_printf ("     ");
	  tty_print_utf8_string (uid->name, uid->len);
	  tty_printf ("\n");
	}
      else if (node->pkt->pkttype == PKT_SIGNATURE)
	{
	  sig = node->pkt->pkt.signature;
	  tty_printf ("   ");
	  tty_printf (_("signed by your key %s on %s%s%s\n"),
		      keystr (sig->keyid), datestr_from_sig (sig), "",
		      sig->flags.exportable ? "" : _(" (non-exportable)"));
	}
    }
  if (!any)
    return 0;			/* none selected */

  if (!cpr_get_answer_is_yes
      ("ask_revoke_sig.okay",
       _("Really create the revocation certificates? (y/N) ")))
    return 0;			/* forget it */

  reason = ask_revocation_reason (0, 1, 0);
  if (!reason)
    {				/* user decided to cancel */
      return 0;
    }

  /* now we can sign the user ids */
reloop:			/* (must use this, because we are modifying the list) */
  primary_pk = keyblock->pkt->pkt.public_key;
  for (node = keyblock; node; node = node->next)
    {
      KBNODE unode;
      PACKET *pkt;
      struct sign_attrib attrib;
      PKT_public_key *signerkey;

      if (!(node->flag & NODFLG_MARK_A)
	  || node->pkt->pkttype != PKT_SIGNATURE)
	continue;
      unode = find_prev_kbnode (keyblock, node, PKT_USER_ID);
      log_assert (unode); /* we already checked this */

      memset (&attrib, 0, sizeof attrib);
      attrib.reason = reason;
      attrib.non_exportable = !node->pkt->pkt.signature->flags.exportable;

      node->flag &= ~NODFLG_MARK_A;
      signerkey = xmalloc_secure_clear (sizeof *signerkey);
      if (get_seckey (ctrl, signerkey, node->pkt->pkt.signature->keyid))
	{
	  log_info (_("no secret key\n"));
          free_public_key (signerkey);
	  continue;
	}
      rc = make_keysig_packet (ctrl, &sig, primary_pk,
			       unode->pkt->pkt.user_id,
			       NULL, signerkey, 0x30, 0, 0,
                               sign_mk_attrib, &attrib, NULL);
      free_public_key (signerkey);
      if (rc)
	{
          write_status_error ("keysig", rc);
	  log_error (_("signing failed: %s\n"), gpg_strerror (rc));
	  release_revocation_reason_info (reason);
	  return changed;
	}
      changed = 1;		/* we changed the keyblock */
      update_trust = 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->flags.revoked = 1;
      pkt = xmalloc_clear (sizeof *pkt);
      pkt->pkttype = PKT_SIGNATURE;
      pkt->pkt.signature = sig;
      insert_kbnode (unode, new_kbnode (pkt), 0);
      goto reloop;
    }

  release_revocation_reason_info (reason);
  return changed;
}


/* 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 signature for the
             mksubpkt argument to make_keysig_packet */
          attrib.reason = (struct revocation_reason_info *)reason;

          rc = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x30,
                                   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
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;
  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
     sig class, but PGP 2.x did not actually implement it, so it would
     probably be safe to use v4 revocations everywhere. -ds */

  for (node = pub_keyblock; node; node = node->next)
    if (pk->version > 3 || (node->pkt->pkttype == PKT_USER_ID &&
			    node->pkt->pkt.user_id->selfsigversion > 3))
      {
	if ((reason = ask_revocation_reason (0, 1, 4)))
	  break;
	else
	  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 = 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))
      {
        int modified = 0;

        /* 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;
            changed = 1;
            goto reloop;
          }
      }

  if (changed)
    commit_kbnode (&pub_keyblock);

leave:
  release_revocation_reason_info (reason);
  return changed;
}


/*
 * Revoke the whole key.
 */
static int
menu_revkey (ctrl_t ctrl, kbnode_t pub_keyblock)
{
  PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key;
  int rc, changed = 0;
  struct revocation_reason_info *reason;
  PACKET *pkt;
  PKT_signature *sig;

  if (pk->flags.revoked)
    {
      tty_printf (_("Key %s is already revoked.\n"), keystr_from_pk (pk));
      return 0;
    }

  reason = ask_revocation_reason (1, 0, 0);
  /* user decided to cancel */
  if (!reason)
    return 0;

  rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk,
			   0x20, 0, 0,
			   revocation_reason_build_cb, reason, NULL);
  if (rc)
    {
      write_status_error ("keysig", rc);
      log_error (_("signing failed: %s\n"), gpg_strerror (rc));
      goto scram;
    }

  changed = 1;			/* we changed the keyblock */

  pkt = xmalloc_clear (sizeof *pkt);
  pkt->pkttype = PKT_SIGNATURE;
  pkt->pkt.signature = sig;
  insert_kbnode (pub_keyblock, new_kbnode (pkt), 0);
  commit_kbnode (&pub_keyblock);

  update_trust = 1;

 scram:
  release_revocation_reason_info (reason);
  return changed;
}


static int
menu_revsubkey (ctrl_t ctrl, kbnode_t pub_keyblock)
{
  PKT_public_key *mainpk;
  KBNODE node;
  int changed = 0;
  int rc;
  struct revocation_reason_info *reason = NULL;

  reason = ask_revocation_reason (1, 0, 0);
  if (!reason)
      return 0; /* User decided to cancel.  */

 reloop: /* (better this way because we are modifying the keyring) */
  mainpk = pub_keyblock->pkt->pkt.public_key;
  for (node = pub_keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
	  && (node->flag & NODFLG_SELKEY))
	{
	  PACKET *pkt;
	  PKT_signature *sig;
	  PKT_public_key *subpk = node->pkt->pkt.public_key;
	  struct sign_attrib attrib;

	  if (subpk->flags.revoked)
	    {
	      tty_printf (_("Subkey %s is already revoked.\n"),
			  keystr_from_pk (subpk));
	      continue;
	    }

	  memset (&attrib, 0, sizeof attrib);
	  attrib.reason = reason;

	  node->flag &= ~NODFLG_SELKEY;
	  rc = make_keysig_packet (ctrl, &sig, mainpk, NULL, subpk, mainpk,
				   0x28, 0, 0, sign_mk_attrib, &attrib,
                                   NULL);
	  if (rc)
	    {
              write_status_error ("keysig", rc);
	      log_error (_("signing failed: %s\n"), gpg_strerror (rc));
	      release_revocation_reason_info (reason);
	      return changed;
	    }
	  changed = 1;		/* we changed the keyblock */

	  pkt = xmalloc_clear (sizeof *pkt);
	  pkt->pkttype = PKT_SIGNATURE;
	  pkt->pkt.signature = sig;
	  insert_kbnode (node, new_kbnode (pkt), 0);
	  goto reloop;
	}
    }
  commit_kbnode (&pub_keyblock);

  /* No need to set update_trust here since signing keys no longer
     are used to certify other keys, so there is no change in trust
     when revoking/removing them */

  release_revocation_reason_info (reason);
  return changed;
}


/* Note that update_ownertrust is going to mark the trustdb dirty when
   enabling or disabling a key.  This is arguably sub-optimal as
   disabled keys are still counted in the web of trust, but perhaps
   not worth adding extra complexity to change. -ds */
#ifndef NO_TRUST_MODELS
static int
enable_disable_key (ctrl_t ctrl, kbnode_t keyblock, int disable)
{
  PKT_public_key *pk =
    find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt.public_key;
  unsigned int trust, newtrust;

  trust = newtrust = get_ownertrust (ctrl, pk);
  newtrust &= ~TRUST_FLAG_DISABLED;
  if (disable)
    newtrust |= TRUST_FLAG_DISABLED;
  if (trust == newtrust)
    return 0;			/* already in that state */
  update_ownertrust (ctrl, pk, newtrust);
  return 0;
}
#endif /*!NO_TRUST_MODELS*/


static void
menu_showphoto (ctrl_t ctrl, kbnode_t keyblock)
{
  KBNODE node;
  int select_all = !count_selected_uids (keyblock);
  int count = 0;
  PKT_public_key *pk = NULL;

  /* Look for the public key first.  We have to be really, really,
     explicit as to which photo this is, and what key it is a UID on
     since people may want to sign it. */

  for (node = keyblock; node; node = node->next)
    {
      if (node->pkt->pkttype == PKT_PUBLIC_KEY)
	pk = node->pkt->pkt.public_key;
      else if (node->pkt->pkttype == PKT_USER_ID)
	{
	  PKT_user_id *uid = node->pkt->pkt.user_id;
	  count++;

	  if ((select_all || (node->flag & NODFLG_SELUID)) &&
	      uid->attribs != NULL)
	    {
	      int i;

	      for (i = 0; i < uid->numattribs; i++)
		{
		  byte type;
		  u32 size;

		  if (uid->attribs[i].type == ATTRIB_IMAGE &&
		      parse_image_header (&uid->attribs[i], &type, &size))
		    {
		      tty_printf (_("Displaying %s photo ID of size %ld for "
				    "key %s (uid %d)\n"),
				  image_type_to_string (type, 1),
				  (ulong) size, keystr_from_pk (pk), count);
		      show_photos (ctrl, &uid->attribs[i], 1, pk, uid);
		    }
		}
	    }
	}
    }
}
