* keyedit.c (sign_uids, keyedit_menu): When the user requests to sign
[gnupg.git] / g10 / keyedit.c
index 433bb9c..7db46cf 100644 (file)
@@ -1,6 +1,6 @@
 /* keyedit.c - keyedit stuff
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
- *               2004 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ *               2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <errno.h>
 #include <assert.h>
 #include <ctype.h>
-
+#ifdef HAVE_LIBREADLINE
+#include <stdio.h>
+#include <readline/readline.h>
+#endif
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
@@ -495,7 +498,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
  */
 static int
 sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
-          int local, int nonrevocable, int trust )
+          int local, int nonrevocable, int trust, int interactive )
 {
     int rc = 0;
     SK_LIST sk_list = NULL;
@@ -503,7 +506,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
     PKT_secret_key *sk = NULL;
     KBNODE node, uidnode;
     PKT_public_key *primary_pk=NULL;
-    int select_all = !count_selected_uids(keyblock);
+    int select_all = !count_selected_uids(keyblock) || interactive;
     int all_v3=1;
 
     /* Are there any non-v3 sigs on this key already? */
@@ -572,10 +575,12 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                    force_v4=0;
                  }
            }
-           else if( node->pkt->pkttype == PKT_USER_ID ) {
+           else if( node->pkt->pkttype == PKT_USER_ID )
+             {
                uidnode = (node->flag & NODFLG_MARK_A)? node : NULL;
                if(uidnode)
                  {
+                   int yesreally=0;
                    char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
                                              uidnode->pkt->pkt.user_id->len,
                                              0);
@@ -598,6 +603,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -624,6 +631,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -649,6 +658,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -658,9 +669,20 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                          }
                      }
 
+                   if(uidnode && interactive && !yesreally)
+                     {
+                       tty_printf(_("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;
+                         }
+                     }
+
                    m_free(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]
@@ -1069,6 +1091,7 @@ change_passphrase( KBNODE keyblock )
     PKT_secret_key *sk;
     char *passphrase = NULL;
     int no_primary_secrets = 0;
+    int any;
 
     node = find_kbnode( keyblock, PKT_SECRET_KEY );
     if( !node ) {
@@ -1077,6 +1100,25 @@ change_passphrase( KBNODE keyblock )
     }
     sk = node->pkt->pkt.secret_key;
 
+    for (any = 0, node=keyblock; node; node = node->next) {
+       if (node->pkt->pkttype == PKT_SECRET_KEY 
+            || node->pkt->pkttype == PKT_SECRET_SUBKEY) {
+           PKT_secret_key *tmpsk = node->pkt->pkt.secret_key;
+            if (!(tmpsk->is_protected
+                  && (tmpsk->protect.s2k.mode == 1001 
+                      || tmpsk->protect.s2k.mode == 1002))) {
+                any = 1;
+                break;
+            }
+        }
+    }
+    if (!any) {
+        tty_printf (_("Key has only stub or on-card key items - "
+                      "no passphrase to change.\n"));
+        goto leave;
+    }
+        
+    /* See how to handle this key.  */
     switch( is_secret_key_protected( sk ) ) {
       case -1:
        rc = G10ERR_PUBKEY_ALGO;
@@ -1089,6 +1131,10 @@ change_passphrase( KBNODE keyblock )
            tty_printf(_("Secret parts of primary key are not available.\n"));
            no_primary_secrets = 1;
        }
+       else if( sk->protect.s2k.mode == 1002 ) {
+           tty_printf(_("Secret parts of primary key are stored on-card.\n"));
+           no_primary_secrets = 1;
+       }
        else {
            tty_printf(_("Key is protected.\n"));
            rc = check_secret_key( sk, 0 );
@@ -1098,14 +1144,18 @@ change_passphrase( KBNODE keyblock )
        break;
     }
 
-    /* unprotect all subkeys (use the supplied passphrase or ask)*/
+    /* Unprotect all subkeys (use the supplied passphrase or ask)*/
     for(node=keyblock; !rc && node; node = node->next ) {
        if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
            PKT_secret_key *subsk = node->pkt->pkt.secret_key;
-           set_next_passphrase( passphrase );
-           rc = check_secret_key( subsk, 0 );
-           if( !rc && !passphrase )
-               passphrase = get_last_passphrase();
+            if ( !(subsk->is_protected
+                   && (subsk->protect.s2k.mode == 1001 
+                       || subsk->protect.s2k.mode == 1002))) {
+                set_next_passphrase( passphrase );
+                rc = check_secret_key( subsk, 0 );
+                if( !rc && !passphrase )
+                    passphrase = get_last_passphrase();
+            }
        }
     }
 
@@ -1149,13 +1199,18 @@ change_passphrase( KBNODE keyblock )
                for(node=keyblock; !rc && node; node = node->next ) {
                    if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
                        PKT_secret_key *subsk = node->pkt->pkt.secret_key;
-                       subsk->protect.algo = dek->algo;
-                       subsk->protect.s2k = *s2k;
-                       rc = protect_secret_key( subsk, dek );
+                        if ( !(subsk->is_protected
+                               && (subsk->protect.s2k.mode == 1001 
+                                   || subsk->protect.s2k.mode == 1002))) {
+                            subsk->protect.algo = dek->algo;
+                            subsk->protect.s2k = *s2k;
+                            rc = protect_secret_key( subsk, dek );
+                        }
                    }
                }
                if( rc )
-                   log_error("protect_secret_key failed: %s\n", g10_errstr(rc) );
+                   log_error("protect_secret_key failed: %s\n",
+                              g10_errstr(rc) );
                else
                    changed++;
                break;
@@ -1244,6 +1299,7 @@ parse_sign_type(const char *str,int *localsig,int *nonrevokesig,int *trustsig)
   return 1;
 }
 
+\f
 /****************
  * 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
@@ -1261,84 +1317,168 @@ parse_sign_type(const char *str,int *localsig,int *nonrevokesig,int *trustsig)
 /* 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, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF,
+    cmdPREFKS, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST,
+    cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, 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") },
+    { "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 },
+    { "sign"    , cmdSIGN      , KEYEDIT_NOT_SK|KEYEDIT_TAIL_MATCH,
+      N_("sign selected user IDs [* see below for related commands]") },
+    { "s"       , cmdSIGN      , KEYEDIT_NOT_SK, 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_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a user ID") },
+    { "addphoto", cmdADDPHOTO  , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a photo ID") },
+    { "deluid"  , cmdDELUID    , KEYEDIT_NOT_SK,
+      N_("delete selected user IDs") },
+    /* delphoto is really deluid in disguise */
+    { "delphoto", cmdDELUID    , KEYEDIT_NOT_SK, NULL },
+
+    { "addkey"  , cmdADDKEY    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a subkey") },
+
+#ifdef ENABLE_CARD_SUPPORT
+    { "addcardkey", cmdADDCARDKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a key to a smartcard") },
+    { "keytocard", cmdKEYTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK, 
+      N_("move a key to a smartcard")},
+    { "bkuptocard", cmdBKUPTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK, 
+      N_("move a backup key to a smartcard")},
+#endif /*ENABLE_CARD_SUPPORT*/
+
+    { "delkey"  , cmdDELKEY    , KEYEDIT_NOT_SK,
+      N_("delete selected subkeys") },
+    { "addrevoker",cmdADDREVOKER,KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a revocation key") },
+    { "delsig"  , cmdDELSIG    , KEYEDIT_NOT_SK,
+      N_("delete signatures from the selected user IDs") },
+    { "expire"  , cmdEXPIRE    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("change the expiration date for the key or selected subkeys") },
+    { "primary" , cmdPRIMARY   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("flag the selected user ID as primary")},
+    { "toggle"  , cmdTOGGLE    , KEYEDIT_NEED_SK,
+      N_("toggle between the secret and public key listings") },
+    { "t"       , cmdTOGGLE    , KEYEDIT_NEED_SK, NULL },
+    { "pref"    , cmdPREF      , KEYEDIT_NOT_SK,
+      N_("list preferences (expert)")},
+    { "showpref", cmdSHOWPREF  , KEYEDIT_NOT_SK,
+      N_("list preferences (verbose)") },
+    { "setpref" , cmdSETPREF   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("set preference list for the selected user IDs") },
+    /* Alias */
+    { "updpref" , cmdSETPREF   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+    { "keyserver",cmdPREFKS    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("set preferred keyserver URL for the selected user IDs")},
+    { "passwd"  , cmdPASSWD    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("change the passphrase") },
+    /* Alias */
+    { "password", cmdPASSWD    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+    { "trust"   , cmdTRUST     , KEYEDIT_NOT_SK, N_("change the ownertrust") },
+    { "revsig"  , cmdREVSIG    , KEYEDIT_NOT_SK,
+      N_("revoke signatures on the selected user IDs") },
+    { "revuid"  , cmdREVUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("revoke selected user IDs") },
+    /* Alias */
+    { "revphoto", cmdREVUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+    { "revkey"  , cmdREVKEY    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("revoke key or selected subkeys") },
+    { "enable"  , cmdENABLEKEY , KEYEDIT_NOT_SK, N_("enable key") },
+    { "disable" , cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key") },
+    { "showphoto",cmdSHOWPHOTO , 0, N_("show selected photo IDs") },
+    { 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. */
+
+  if(start==0)
+    return rl_completion_matches(text,command_generator);
+
+  rl_attempted_completion_over=1;
+
+  return NULL;
+}
+#endif /* HAVE_LIBREADLINE */
+
+
 void
 keyedit_menu( const char *username, STRLIST locusr,
              STRLIST commands, int quiet, int seckey_check )
 {
-  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, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF,
-      cmdPREFKS, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST,
-      cmdADDCARDKEY, cmdKEYTOCARD,
-      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 fingerprint") },
-      { "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_("list signatures") },
-      { "c"       , cmdCHECK     , 0, NULL },
-      { "sign"    , cmdSIGN      , KEYEDIT_NOT_SK|KEYEDIT_TAIL_MATCH, N_("sign selected user IDs") },
-      { "s"       , cmdSIGN      , KEYEDIT_NOT_SK, NULL },
-      /* "lsign" will never match since "sign" comes first and it is a
-        tail match.  It is just here so it shows up in the help
-        menu. */
-      { "lsign"   , cmdNOP       , 0, N_("sign selected user IDs locally") },
-      { "debug"   , cmdDEBUG     , 0, NULL },
-      { "adduid"  , cmdADDUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("add a user ID") },
-      { "addphoto", cmdADDPHOTO  , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("add a photo ID") },
-      { "deluid"  , cmdDELUID    , KEYEDIT_NOT_SK, N_("delete user ID") },
-      /* delphoto is really deluid in disguise */
-      { "delphoto", cmdDELUID    , KEYEDIT_NOT_SK, NULL },
-      { "addkey"  , cmdADDKEY    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("add a subkey") },
-#ifdef ENABLE_CARD_SUPPORT
-      { "addcardkey", cmdADDCARDKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("add a key to a smartcard") },
-      { "keytocard", cmdKEYTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK, N_("move a key to a smartcard")},
-#endif /*ENABLE_CARD_SUPPORT*/
-      { "delkey"  , cmdDELKEY    , KEYEDIT_NOT_SK, N_("delete selected subkeys") },
-      { "addrevoker",cmdADDREVOKER,KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("add a revocation key") },
-      { "delsig"  , cmdDELSIG    , KEYEDIT_NOT_SK, N_("delete signatures") },
-      { "expire"  , cmdEXPIRE    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("change the expiration date") },
-      { "primary" , cmdPRIMARY   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("flag a user ID as primary")},
-      { "toggle"  , cmdTOGGLE    , KEYEDIT_NEED_SK, N_("toggle between secret and public key listing") },
-      { "t"       , cmdTOGGLE    , KEYEDIT_NEED_SK, NULL },
-      { "pref"    , cmdPREF      , KEYEDIT_NOT_SK, N_("list preferences (expert)")},
-      { "showpref", cmdSHOWPREF  , KEYEDIT_NOT_SK, N_("list preferences (verbose)") },
-      { "setpref" , cmdSETPREF   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("set preference list") },
-      /* Alias */
-      { "updpref" , cmdSETPREF   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
-      { "keyserver",cmdPREFKS    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("set preferred keyserver URL")},
-      { "passwd"  , cmdPASSWD    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("change the passphrase") },
-      /* Alias */
-      { "password", cmdPASSWD    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
-      { "trust"   , cmdTRUST     , KEYEDIT_NOT_SK, N_("change the ownertrust") },
-      { "revsig"  , cmdREVSIG    , KEYEDIT_NOT_SK, N_("revoke signatures") },
-      { "revuid"  , cmdREVUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("revoke selected user IDs") },
-      /* Alias */
-      { "revphoto", cmdREVUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
-      { "revkey"  , cmdREVKEY    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, N_("revoke selected subkeys") },
-      { "disable" , cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key") },
-      { "enable"  , cmdENABLEKEY , KEYEDIT_NOT_SK, N_("enable key") },
-      { "showphoto",cmdSHOWPHOTO , 0, N_("show photo ID") },
-      { NULL, cmdNONE, 0, NULL }
-    };
     enum cmdids cmd = 0;
     int rc = 0;
     KBNODE keyblock = NULL;
@@ -1361,7 +1501,20 @@ keyedit_menu( const char *username, STRLIST locusr,
        goto leave;
       }
 
-    /* get the public key */
+#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 trough a call to
+       get_validity ().  */
+    check_trustdb_stale ();
+#endif
+
+    /* Get the public key */
     rc = get_pubkey_byname (NULL, username, &keyblock, &kdbhd, 1);
     if( rc )
        goto leave;
@@ -1420,6 +1573,7 @@ keyedit_menu( const char *username, STRLIST locusr,
        PKT_public_key *pk=keyblock->pkt->pkt.public_key;
 
        tty_printf("\n");
+
        if( redisplay && !quiet )
          {
            show_key_with_all_names( cur_keyblock, 0, 1, 0, 1, 0 );
@@ -1439,10 +1593,13 @@ keyedit_menu( const char *username, STRLIST locusr,
                else
                    have_commands = 0;
            }
-           if( !have_commands ) {
+           if( !have_commands )
+             {
+               tty_enable_completion(keyedit_completion);
                answer = cpr_get_no_help("keyedit.prompt", _("Command> "));
                cpr_kill_prompt();
-           }
+               tty_disable_completion();
+             }
            trim_spaces(answer);
        } while( *answer == '#' );
 
@@ -1499,15 +1656,24 @@ keyedit_menu( const char *username, STRLIST locusr,
            else
              cmd = cmds[i].id;
        }
-       switch( cmd )  {
+       switch( cmd )
+         {
          case cmdHELP:
            for(i=0; cmds[i].name; i++ )
              {
                if((cmds[i].flags & KEYEDIT_NEED_SK) && !sec_keyblock )
                  ; /* skip if we do not have the secret key */
                else if( cmds[i].desc )
-                 tty_printf("%-10s %s\n", cmds[i].name, _(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:
@@ -1537,7 +1703,7 @@ keyedit_menu( const char *username, STRLIST locusr,
 
          case cmdSIGN: /* sign (only the public key) */
            {
-             int localsig=0,nonrevokesig=0,trustsig=0;
+             int localsig=0,nonrevokesig=0,trustsig=0,interactive=0;
 
              if( pk->is_revoked )
                {
@@ -1558,16 +1724,11 @@ keyedit_menu( const char *username, STRLIST locusr,
                    }
                }
 
-             if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) )
-               {
-                 if( !cpr_get_answer_is_yes("keyedit.sign_all.okay",
-                                            _("Really sign all user IDs?"
-                                              " (y/N) ")))
-                   {
-                     tty_printf(_("Hint: Select the user IDs to sign\n"));
-                     break;
-                   }
-               }
+             if(count_uids(keyblock) > 1 && !count_selected_uids(keyblock)
+                && !cpr_get_answer_is_yes("keyedit.sign_all.okay",
+                                          _("Really sign all user IDs?"
+                                            " (y/N) ")))
+               interactive=1;
 
              /* What sort of signing are we doing? */
              if(!parse_sign_type(answer,&localsig,&nonrevokesig,&trustsig))
@@ -1577,7 +1738,7 @@ keyedit_menu( const char *username, STRLIST locusr,
                }
 
              sign_uids(keyblock, locusr, &modified,
-                       localsig, nonrevokesig, trustsig);
+                       localsig, nonrevokesig, trustsig, interactive);
            }
            break;
 
@@ -1698,6 +1859,69 @@ keyedit_menu( const char *username, STRLIST locusr,
              }
          }
           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;
+            const char *fname;
+            PACKET *pkt;
+            IOBUF a;
+
+            fname = arg_string;
+            if (!*fname)
+              {
+                tty_printf (_("Command expects a filename argument\n"));
+                break;
+              }
+
+            /* Open that file.  */
+            a = iobuf_open (fname);
+            if (a && is_secured_file (iobuf_get_fd (a)))
+              {
+                iobuf_close (a);
+                a = NULL;
+                errno = EPERM;
+              }
+            if (!a)
+              {
+               tty_printf (_("Can't open `%s': %s\n"),
+                            fname, strerror(errno));
+                break;
+              }
+            
+            /* Parse and check that file.  */
+            pkt = xmalloc (sizeof *pkt);
+            init_packet (pkt);
+            rc = parse_packet (a, pkt);
+            iobuf_close (a);
+            iobuf_ioctl (NULL, 2, 0, (char*)fname); /* (invalidate cache).  */
+            if (!rc 
+                && pkt->pkttype != PKT_SECRET_KEY 
+                && pkt->pkttype != PKT_SECRET_SUBKEY)
+              rc = G10ERR_NO_SECKEY;
+            if (rc)
+              {
+                tty_printf(_("Error reading backup key from `%s': %s\n"),
+                           fname, g10_errstr (rc));
+                free_packet (pkt);
+                xfree (pkt);
+                break;
+              }
+            node = new_kbnode (pkt);
+
+            /* Store it.  */
+            if (card_store_subkey (node, 0))
+              {
+                redisplay = 1;
+                sec_modified = 1;
+              }
+            release_kbnode (node);
+         }
+          break;
+
 #endif /* ENABLE_CARD_SUPPORT */
 
          case cmdDELKEY: {
@@ -1958,6 +2182,7 @@ keyedit_menu( const char *username, STRLIST locusr,
 }
 
 
+\f
 /****************
  * show preferences of a public keyblock.
  */
@@ -2310,6 +2535,15 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
                primary=pk;
            }
 
+           if(pk->is_revoked)
+             {
+               char *user=get_user_id_string_native(pk->revoked.keyid);
+               const char *algo=pubkey_algo_to_string(pk->revoked.algo);
+               tty_printf(_("This key was revoked on %s by %s key %s\n"),
+                          revokestr_from_pk(pk),algo?algo:"?",user);
+               m_free(user);
+             }
+
            if(with_revoker)
              {
                if( !pk->revkey && pk->numrevkeys )
@@ -3551,7 +3785,8 @@ menu_set_keyserver_url (const char *url,
        {
          PKT_signature *sig = node->pkt->pkt.signature;
          if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
-              && (uid && (sig->sig_class&~3) == 0x10) )
+              && (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 )