Some bug fixes of the last release
[gnupg.git] / g10 / keyedit.c
index 3851b5c..2d3a0d0 100644 (file)
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <ctype.h>
 
 #include "options.h"
 #include "packet.h"
 #include "memory.h"
 #include "util.h"
 #include "main.h"
+#include "trustdb.h"
 #include "filter.h"
 #include "ttyio.h"
+#include "status.h"
 #include "i18n.h"
 
+static void show_prefs( KBNODE keyblock, PKT_user_id *uid );
+static void show_key_with_all_names( KBNODE keyblock,
+           int only_marked, int with_fpr, int with_subkeys, int with_prefs );
+static void show_key_and_fingerprint( KBNODE keyblock );
+static void show_fingerprint( PKT_public_key *pk );
+static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock );
+static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_select_uid( KBNODE keyblock, int index );
+static int menu_select_key( KBNODE keyblock, int index );
+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 count_selected_keys( KBNODE keyblock );
 
+#define CONTROL_D ('D' - 'A' + 1)
 
-static void
-show_fingerprint( PKT_public_key *pk )
-{
-    byte *array, *p;
-    size_t i, n;
+#define NODFLG_BADSIG (1<<0)  /* bad signature */
+#define NODFLG_NOKEY  (1<<1)  /* no public key */
+#define NODFLG_SIGERR (1<<2)  /* other sig error */
 
-    p = array = fingerprint_from_pk( pk, &n );
-    tty_printf("             Fingerprint:");
-    if( n == 20 ) {
-       for(i=0; i < n ; i++, i++, p += 2 ) {
-           if( i == 10 )
-               tty_printf(" ");
-           tty_printf(" %02X%02X", *p, p[1] );
-       }
-    }
-    else {
-       for(i=0; i < n ; i++, p++ ) {
-           if( i && !(i%8) )
-               tty_printf(" ");
-           tty_printf(" %02X", *p );
-       }
-    }
-    tty_printf("\n");
-    m_free(array);
-}
+#define NODFLG_MARK_A (1<<4)  /* temporary mark */
+
+#define NODFLG_SELUID (1<<8)  /* indicate the selected userid */
+#define NODFLG_SELKEY (1<<9)  /* indicate the selected key */
 
 
-/****************
- * Ask whether the user is willing to sign the key. Return true if so.
- */
 static int
-sign_it_p( PKT_public_key *pk, PKT_user_id *uid )
+get_keyblock_byname( KBNODE *keyblock, KBPOS *kbpos, const char *username )
 {
-    char *answer;
-    int yes;
-
-    tty_printf("\nAre you really sure that you want to sign this key:\n\n"
-              "%4u%c/%08lX %s ",
-             nbits_from_pk( pk ),
-             pubkey_letter( pk->pubkey_algo ),
-             (ulong)keyid_from_pk( pk, NULL ),
-             datestr_from_pk( pk )               );
-    tty_print_string( uid->name, uid->len );
-    tty_printf("\n");
-    show_fingerprint(pk);
-    tty_printf("\n");
-    answer = tty_get("Sign this key? ");
-    tty_kill_prompt();
-    yes = answer_is_yes(answer);
-    m_free(answer);
-    return yes;
+    int rc;
+
+    *keyblock = NULL;
+    /* search the userid */
+    rc = find_keyblock_byname( kbpos, username );
+    if( rc ) {
+       log_error(_("%s: user not found\n"), username );
+       return rc;
+    }
+
+    /* read the keyblock */
+    rc = read_keyblock( kbpos, keyblock );
+    if( rc )
+       log_error("%s: keyblock read problem: %s\n", username, g10_errstr(rc));
+    else
+       merge_keys_and_selfsig( *keyblock );
+
+    return rc;
 }
 
 
 /****************
  * Check the keysigs and set the flags to indicate errors.
- * Usage of nodes flag bits:
- * Bit 0 = bad signature
- *     1 = no public key
- *     2 = other error
  * Returns true if error found.
  */
 static int
-check_all_keysigs( KBNODE keyblock )
+check_all_keysigs( KBNODE keyblock, int only_selected )
 {
     KBNODE kbctx;
     KBNODE node;
@@ -110,541 +106,274 @@ check_all_keysigs( KBNODE keyblock )
     int inv_sigs = 0;
     int no_key = 0;
     int oth_err = 0;
+    int has_selfsig = 0;
+    int mis_selfsig = 0;
+    int selected = !only_selected;
+    int anyuid = 0;
 
     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-       if( node->pkt->pkttype == PKT_SIGNATURE
-           && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
-           PKT_signature *sig = node->pkt->pkt.signature;
-           int sigrc;
-
-           tty_printf("sig");
-           switch( (rc = check_key_signature( keyblock, node,NULL)) ) {
-             case 0:                node->flag = 0; sigrc = '!'; break;
-             case G10ERR_BAD_SIGN:  inv_sigs++; node->flag = 1; sigrc = '-'; break;
-             case G10ERR_NO_PUBKEY: no_key++;   node->flag = 2; sigrc = '?'; break;
-             default:               oth_err++;  node->flag = 4; sigrc = '%'; break;
-           }
-           tty_printf("%c       %08lX %s   ",
-                   sigrc, sig->keyid[1], datestr_from_sig(sig));
-           if( sigrc == '%' )
-               tty_printf("[%s] ", g10_errstr(rc) );
-           else if( sigrc == '?' )
-               ;
-           else {
-               size_t n;
-               char *p = get_user_id( sig->keyid, &n );
-               tty_print_string( p, n > 40? 40 : n );
-               m_free(p);
+       if( node->pkt->pkttype == PKT_USER_ID ) {
+           PKT_user_id *uid = node->pkt->pkt.user_id;
+
+           if( only_selected )
+               selected = (node->flag & NODFLG_SELUID);
+           if( selected ) {
+               tty_printf("uid  ");
+               tty_print_string( uid->name, uid->len );
+               tty_printf("\n");
+               if( anyuid && !has_selfsig )
+                   mis_selfsig++;
+               has_selfsig = 0;
+               anyuid = 1;
            }
-           tty_printf("\n");
-           /* FIXME: update the trustdb */
        }
-    }
-    if( inv_sigs )
-       tty_printf("%d bad signatures\n", inv_sigs );
-    if( no_key )
-       tty_printf("No public key for %d signatures\n", no_key );
-    if( oth_err )
-       tty_printf("%d signatures not checked due to errors\n", oth_err );
-    return inv_sigs || no_key || oth_err;
-}
-
-
-/****************
- * Ask and remove invalid signatures that are to be removed.
- */
-static int
-remove_keysigs( KBNODE keyblock, u32 *keyid, int all )
-{
-    KBNODE kbctx;
-    KBNODE node;
-    char *answer;
-    int yes;
-    int count;
-
-    count = 0;
-    for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-       if( ((node->flag & 7) || all )
-           && node->pkt->pkttype == PKT_SIGNATURE
-           && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
+       else if( selected && node->pkt->pkttype == PKT_SIGNATURE
+                && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
            PKT_signature *sig = node->pkt->pkt.signature;
+           int sigrc, selfsig;
 
-           tty_printf("\n \"%08lX %s   ",
-                       sig->keyid[1], datestr_from_sig(sig));
-           if( node->flag & 6 )
-               tty_printf("[User name not available] ");
-           else {
-               size_t n;
-               char *p = get_user_id( sig->keyid, &n );
-               tty_print_string( p, n );
-               m_free(p);
-           }
-           tty_printf("\"\n");
-           if( node->flag & 1 )
-               tty_printf("This is a BAD signature!\n");
-           else if( node->flag & 2 )
-               tty_printf("Public key not available.\n");
-           else if( node->flag & 4 )
-               tty_printf("The signature could not be checked!\n");
-
-           if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
-               tty_printf("Skipped self-signature\n");
-               continue; /* do not remove self-signatures */
+           switch( (rc = check_key_signature( keyblock, node, &selfsig)) ) {
+             case 0:
+               node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR);
+               sigrc = '!';
+               break;
+             case G10ERR_BAD_SIGN:
+               node->flag = NODFLG_BADSIG;
+               sigrc = '-';
+               inv_sigs++;
+               break;
+             case G10ERR_NO_PUBKEY:
+               node->flag = NODFLG_NOKEY;
+               sigrc = '?';
+               no_key++;
+               break;
+             default:
+               node->flag = NODFLG_SIGERR;
+               sigrc = '%';
+               oth_err++;
+               break;
            }
-
-           answer = tty_get("\nRemove this signature? ");
-           tty_kill_prompt();
-           if( answer_is_yes(answer) ) {
-               node->flag |= 128;     /* use bit 7 to mark this node */
-               count++;
+           if( sigrc != '?' ) {
+               tty_printf("sig%c       %08lX %s   ",
+                       sigrc, sig->keyid[1], datestr_from_sig(sig));
+               if( sigrc == '%' )
+                   tty_printf("[%s] ", g10_errstr(rc) );
+               else if( sigrc == '?' )
+                   ;
+               else if( selfsig ) {
+                   tty_printf( _("[self-signature]") );
+                   if( sigrc == '!' )
+                       has_selfsig = 1;
+               }
+               else {
+                   size_t n;
+                   char *p = get_user_id( sig->keyid, &n );
+                   tty_print_string( p, n > 40? 40 : n );
+                   m_free(p);
+               }
+               tty_printf("\n");
+               /* fixme: Should we update the trustdb here */
            }
-           m_free(answer);
        }
     }
-
-    if( !count )
-       return 0; /* nothing to remove */
-    answer = tty_get("Do you really want to remove the selected signatures? ");
-    tty_kill_prompt();
-    yes = answer_is_yes(answer);
-    m_free(answer);
-    if( !yes )
-       return 0;
-
-    for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 1)) ; ) {
-       if( node->flag & 128)
-           delete_kbnode(node );
-    }
-
-    return 1;
+    if( !has_selfsig )
+       mis_selfsig++;
+    if( inv_sigs == 1 )
+       tty_printf(_("1 bad signature\n"), inv_sigs );
+    else if( inv_sigs )
+       tty_printf(_("%d bad signatures\n"), inv_sigs );
+    if( no_key == 1 )
+       tty_printf(_("1 signature not checked due to a missing key\n") );
+    else if( no_key )
+       tty_printf(_("%d signatures not checked due to missing keys\n"), no_key );
+    if( oth_err == 1 )
+       tty_printf(_("1 signature not checked due to an error\n") );
+    else if( oth_err )
+       tty_printf(_("%d signatures not checked due to errors\n"), oth_err );
+    if( mis_selfsig == 1 )
+       tty_printf(_("1 user id without valid self-signature detected\n"));
+    else if( mis_selfsig  )
+       tty_printf(_("%d user ids without valid self-signatures detected\n"),
+                                                                   mis_selfsig);
+
+    return inv_sigs || no_key || oth_err || mis_selfsig;
 }
 
 
 /****************
- * This function signs the key of USERNAME with all users listed in
- * LOCUSR. If LOCUSR is NULL the default secret certificate will
- * be used.  This works on all keyrings, so there is no armor or
- * compress stuff here.
+ * Loop over all locusr and and sign the uids after asking.
+ * If no user id is marked, all user ids will be signed;
+ * if some user_ids are marked those will be signed.
+ *
+ * fixme: Add support for our proposed sign-all scheme
  */
-int
-sign_key( const char *username, STRLIST locusr )
+static int
+sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
 {
-    md_filter_context_t mfx;
     int rc = 0;
     SK_LIST sk_list = NULL;
     SK_LIST sk_rover = NULL;
-    KBNODE keyblock = NULL;
-    KBNODE kbctx, node;
-    KBPOS kbpos;
-    PKT_public_key *pk;
-    u32 pk_keyid[2];
-    char *answer;
-
-    memset( &mfx, 0, sizeof mfx);
-
-    /* search the userid */
-    rc = find_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error("user '%s' not found\n", username );
-       goto leave;
-    }
+    PKT_secret_key *sk = NULL;
+    KBNODE node, uidnode;
+    PKT_public_key *primary_pk=NULL;
+    int select_all = !count_selected_uids(keyblock);
+    int upd_trust = 0;
 
     /* build a list of all signators */
     rc=build_sk_list( locusr, &sk_list, 0, 1 );
     if( rc )
        goto leave;
 
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("error reading the certificate: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error("Oops; public key not found anymore!\n");
-       rc = G10ERR_GENERAL;
-       goto leave;
-    }
-
-    pk = node->pkt->pkt.public_key;
-    keyid_from_pk( pk, pk_keyid );
-    log_info("Checking signatures of this public key certificate:\n");
-    tty_printf("pub  %4u%c/%08lX %s   ",
-             nbits_from_pk( pk ),
-             pubkey_letter( pk->pubkey_algo ),
-             pk_keyid[1], datestr_from_pk(pk) );
-    {
+    /* loop over all signaturs */
+    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+       u32 sk_keyid[2];
        size_t n;
-       char *p = get_user_id( pk_keyid, &n );
-       tty_print_string( p, n > 40? 40 : n );
-       m_free(p);
-       tty_printf("\n");
-    }
+       char *p;
 
-    clear_kbnode_flags( keyblock );
-    if( check_all_keysigs( keyblock ) ) {
-       if( !opt.batch ) {
-           /* ask whether we really should do anything */
-           answer = tty_get("To you want to remove some of the invalid sigs? ");
-           tty_kill_prompt();
-           if( answer_is_yes(answer) )
-               remove_keysigs( keyblock, pk_keyid, 0 );
-           m_free(answer);
+       /* we have to use a copy of the sk, because make_keysig_packet
+        * may remove the protection from sk and if we did other
+        * changes to the secret key, we would save the unprotected
+        * version */
+       if( sk )
+           free_secret_key(sk);
+       sk = copy_secret_key( NULL, sk_rover->sk );
+       keyid_from_sk( sk, 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;
        }
-    }
-
-    /* check whether we it is possible to sign this key */
-    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
-       u32 akeyid[2];
-
-       keyid_from_sk( sk_rover->sk, akeyid );
-       for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-           if( node->pkt->pkttype == PKT_USER_ID )
-               sk_rover->mark = 1;
-           else if( node->pkt->pkttype == PKT_SIGNATURE
+       /* reset mark for uids which are already signed */
+       uidnode = NULL;
+       for( node=keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_USER_ID ) {
+               uidnode = (node->flag & NODFLG_MARK_A)? node : NULL;
+           }
+           else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE
                && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
-               if( akeyid[0] == node->pkt->pkt.signature->keyid[0]
-                   && akeyid[1] == node->pkt->pkt.signature->keyid[1] ) {
-                   log_info("Already signed by keyid %08lX\n",
-                                                       (ulong)akeyid[1] );
-                   sk_rover->mark = 0;
+               if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0]
+                   && sk_keyid[1] == node->pkt->pkt.signature->keyid[1] ) {
+                   tty_printf(_("Already signed by key %08lX\n"),
+                                                       (ulong)sk_keyid[1] );
+                   uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */
                }
            }
        }
-    }
-    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
-       if( sk_rover->mark )
-           break;
-    }
-    if( !sk_rover ) {
-       log_info("Nothing to sign\n");
-       goto leave;
-    }
-
-    /* Loop over all signers and all user ids and sign */
-    /* FIXME: we have to change it: Present all user-ids and
-     * then ask whether all those ids shall be signed if the user
-     * answers yes, go and make a 0x14 sign class packet and remove
-     * old one-user-id-only-sigs (user should be noted of this
-     * condition while presenting the user-ids); if he had answered
-     * no, present each user-id in turn and ask which one should be signed
-     * (only one) - if there is already a single-user-sig, do nothing.
-     * (this is propably already out in the world) */
-    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
-       if( !sk_rover->mark )
+       /* check whether any uids are left for signing */
+       if( !count_uids_with_flag(keyblock, NODFLG_MARK_A) ) {
+           tty_printf(_("Nothing to sign with key %08lX\n"),
+                                                 (ulong)sk_keyid[1] );
            continue;
-       for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-           if( node->pkt->pkttype == PKT_USER_ID ) {
-               if( sign_it_p( pk, node->pkt->pkt.user_id ) ) {
-                   PACKET *pkt;
-                   PKT_signature *sig;
-
-                   rc = make_keysig_packet( &sig, pk,
-                                                  node->pkt->pkt.user_id,
-                                                  NULL,
-                                                  sk_rover->sk,
-                                                  0x10, 0, NULL, NULL );
-                   if( rc ) {
-                       log_error("make_keysig_packet failed: %s\n", g10_errstr(rc));
-                       goto leave;
-                   }
-
-                   pkt = m_alloc_clear( sizeof *pkt );
-                   pkt->pkttype = PKT_SIGNATURE;
-                   pkt->pkt.signature = sig;
-                   insert_kbnode( node, new_kbnode(pkt), PKT_USER_ID );
+       }
+       /* Ask whether we really should sign these user id(s) */
+       tty_printf("\n");
+       show_key_with_all_names( keyblock, 1, 1, 0, 0 );
+       tty_printf("\n");
+       tty_printf(_(
+            "Are you really sure that you want to sign this key\n"
+            "with your key: \""));
+       p = get_user_id( sk_keyid, &n );
+       tty_print_string( p, n );
+       m_free(p); p = NULL;
+       tty_printf("\"\n\n");
+
+       if( !cpr_get_answer_is_yes(N_("sign_uid.okay"), _("Really sign? ")) )
+           continue;;
+       /* now we can sign the user ids */
+      reloop: /* (must use this, because we are modifing 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;
+
+               assert( primary_pk );
+               node->flag &= ~NODFLG_MARK_A;
+               rc = make_keysig_packet( &sig, primary_pk,
+                                              node->pkt->pkt.user_id,
+                                              NULL,
+                                              sk,
+                                              0x10, 0, NULL, NULL );
+               if( rc ) {
+                   log_error(_("signing failed: %s\n"), g10_errstr(rc));
+                   goto leave;
                }
+               *ret_modified = 1; /* we changed the keyblock */
+               upd_trust = 1;
+
+               pkt = m_alloc_clear( sizeof *pkt );
+               pkt->pkttype = PKT_SIGNATURE;
+               pkt->pkt.signature = sig;
+               insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE );
+               goto reloop;
            }
        }
+    } /* end loop over signators */
+    if( upd_trust && primary_pk ) {
+       rc = clear_trust_checked_flag( primary_pk );
     }
 
-    rc = update_keyblock( &kbpos, keyblock );
-    if( rc ) {
-       log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
 
   leave:
-    release_kbnode( keyblock );
     release_sk_list( sk_list );
-    md_close( mfx.md );
+    if( sk )
+       free_secret_key(sk);
     return rc;
 }
 
 
 
-int
-edit_keysigs( const char *username )
-{
-    int rc = 0;
-    KBNODE keyblock = NULL;
-    KBNODE node;
-    KBPOS kbpos;
-    PKT_public_key *pk;
-    u32 pk_keyid[2];
-
-    /* search the userid */
-    rc = find_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error("%s: user not found\n", username );
-       goto leave;
-    }
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("%s: certificate read problem: %s\n", username, g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error("Oops; public key not found anymore!\n");
-       rc = G10ERR_GENERAL;
-       goto leave;
-    }
-
-    pk = node->pkt->pkt.public_key;
-    keyid_from_pk( pk, pk_keyid );
-    log_info("Checking signatures of this public key certificate:\n");
-    tty_printf("pub  %4u%c/%08lX %s   ",
-             nbits_from_pk( pk ),
-             pubkey_letter( pk->pubkey_algo ),
-             pk_keyid[1], datestr_from_pk(pk) );
-    {
-       size_t n;
-       char *p = get_user_id( pk_keyid, &n );
-       tty_print_string( p, n > 40? 40 : n );
-       m_free(p);
-       tty_printf("\n");
-    }
-
-    clear_kbnode_flags( keyblock );
-    check_all_keysigs( keyblock );
-    if( remove_keysigs( keyblock, pk_keyid, 1 ) ) {
-       rc = update_keyblock( &kbpos, keyblock );
-       if( rc ) {
-           log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
-           goto leave;
-       }
-    }
-
-  leave:
-    release_kbnode( keyblock );
-    return rc;
-}
-
-
 /****************
- * Delete a public or secret key from a keyring.
+ * Change the passphrase of the primary and all secondary keys.
+ * We use only one passphrase for all keys.
  */
-int
-delete_key( const char *username, int secret )
-{
-    int rc = 0;
-    KBNODE keyblock = NULL;
-    KBNODE node;
-    KBPOS kbpos;
-    PKT_public_key *pk = NULL;
-    PKT_secret_key *sk = NULL;
-    u32 keyid[2];
-    int okay=0;
-
-    /* search the userid */
-    rc = secret? find_secret_keyblock_byname( &kbpos, username )
-              : find_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error("%s: user not found\n", username );
-       goto leave;
-    }
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("%s: read problem: %s\n", username, g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, secret? PKT_SECRET_KEY:PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error("Oops; key not found anymore!\n");
-       rc = G10ERR_GENERAL;
-       goto leave;
-    }
-
-    if( secret ) {
-       sk = node->pkt->pkt.secret_key;
-       keyid_from_sk( sk, keyid );
-    }
-    else {
-       pk = node->pkt->pkt.public_key;
-       keyid_from_pk( pk, keyid );
-       rc = seckey_available( keyid );
-       if( !rc ) {
-           log_error(_(
-           "there is a secret key for this public key!\n"));
-           log_info(_(
-           "use option \"--delete-secret-key\" to delete it first.\n"));
-           rc = -1;
-       }
-       else if( rc != G10ERR_NO_SECKEY )
-           log_error("%s: get secret key: %s\n", username, g10_errstr(rc) );
-       else
-           rc = 0;
-    }
-
-    if( rc )
-       rc = 0;
-    else if( opt.batch && secret )
-       log_error(_("can't do that in batch-mode\n"));
-    else if( opt.batch && opt.answer_yes )
-       okay++;
-    else if( opt.batch )
-       log_error(_("can't do that in batch-mode without \"--yes\"\n"));
-    else {
-       char *p;
-       size_t n;
-
-       if( secret )
-           tty_printf("sec  %4u%c/%08lX %s   ",
-                     nbits_from_sk( sk ),
-                     pubkey_letter( sk->pubkey_algo ),
-                     keyid[1], datestr_from_sk(sk) );
-       else
-           tty_printf("pub  %4u%c/%08lX %s   ",
-                     nbits_from_pk( pk ),
-                     pubkey_letter( pk->pubkey_algo ),
-                     keyid[1], datestr_from_pk(pk) );
-       p = get_user_id( keyid, &n );
-       tty_print_string( p, n );
-       m_free(p);
-       tty_printf("\n\n");
-
-       p = tty_get(_("Delete this key from the keyring? "));
-       tty_kill_prompt();
-       if( secret && answer_is_yes(p)) {
-           /* I think it is not required to check a passphrase; if
-            * the user is so stupid as to let others access his secret keyring
-            * (and has no backup) - it is up him to read some very
-            * basic texts about security.
-            */
-           m_free(p);
-           p = tty_get(_("This is a secret key! - really delete? "));
-       }
-       if( answer_is_yes(p) )
-           okay++;
-       m_free(p);
-    }
-
-
-    if( okay ) {
-       rc = delete_keyblock( &kbpos );
-       if( rc ) {
-           log_error("delete_keyblock failed: %s\n", g10_errstr(rc) );
-           goto leave;
-       }
-    }
-
-  leave:
-    release_kbnode( keyblock );
-    return rc;
-}
-
-
-int
-change_passphrase( const char *username )
+static int
+change_passphrase( KBNODE keyblock )
 {
     int rc = 0;
-    KBNODE keyblock = NULL;
+    int changed=0;
     KBNODE node;
-    KBPOS kbpos;
     PKT_secret_key *sk;
-    u32 keyid[2];
-    char *answer;
-    int changed=0;
     char *passphrase = NULL;
 
-    /* find the userid */
-    rc = find_secret_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error("secret key for user '%s' not found\n", username );
-       goto leave;
-    }
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("error reading the certificate: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
     node = find_kbnode( keyblock, PKT_SECRET_KEY );
     if( !node ) {
        log_error("Oops; secret key not found anymore!\n");
-       rc = G10ERR_GENERAL;
        goto leave;
     }
-
     sk = node->pkt->pkt.secret_key;
-    keyid_from_sk( sk, keyid );
-    tty_printf("sec  %4u%c/%08lX %s   ",
-             nbits_from_sk( sk ),
-             pubkey_letter( sk->pubkey_algo ),
-             keyid[1], datestr_from_sk(sk) );
-    {
-       size_t n;
-       char *p = get_user_id( keyid, &n );
-       tty_print_string( p, n );
-       m_free(p);
-       tty_printf("\n");
-    }
-    for(node=keyblock; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
-           PKT_secret_key *subsk = node->pkt->pkt.secret_key;
-           keyid_from_sk( subsk, keyid );
-           tty_printf("sub  %4u%c/%08lX %s\n",
-                     nbits_from_sk( subsk ),
-                     pubkey_letter( subsk->pubkey_algo ),
-                     keyid[1], datestr_from_sk(subsk) );
-       }
-    }
 
-    clear_kbnode_flags( keyblock );
     switch( is_secret_key_protected( sk ) ) {
       case -1:
        rc = G10ERR_PUBKEY_ALGO;
        break;
       case 0:
-       tty_printf("This key is not protected.\n");
+       tty_printf(_("This key is not protected.\n"));
        break;
       default:
-       tty_printf("Key is protected.\n");
-       rc = check_secret_key( sk );
+       tty_printf(_("Key is protected.\n"));
+       rc = check_secret_key( sk, 0 );
        if( !rc )
            passphrase = get_last_passphrase();
        break;
     }
 
     /* unprotect all subkeys (use the supplied passphrase or ask)*/
-    for(node=keyblock; node; node = node->next ) {
+    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 );
-           if( rc )
-               break;
+           rc = check_secret_key( subsk, 0 );
        }
     }
 
     if( rc )
-       tty_printf("Can't edit this key: %s\n", g10_errstr(rc));
+       tty_printf(_("Can't edit this key: %s\n"), g10_errstr(rc));
     else {
        DEK *dek = NULL;
        STRING2KEY *s2k = m_alloc_secure( sizeof *s2k );
@@ -653,9 +382,9 @@ change_passphrase( const char *username )
 
        set_next_passphrase( NULL );
        for(;;) {
-           s2k->mode = 1;
-           s2k->hash_algo = DIGEST_ALGO_RMD160;
-           dek = passphrase_to_dek( NULL, CIPHER_ALGO_BLOWFISH, s2k, 2 );
+           s2k->mode = opt.s2k_mode;
+           s2k->hash_algo = opt.s2k_digest_algo;
+           dek = passphrase_to_dek( NULL, opt.s2k_cipher_algo, s2k, 2 );
            if( !dek ) {
                tty_printf(_("passphrase not correctly repeated; try again.\n"));
            }
@@ -663,11 +392,9 @@ change_passphrase( const char *username )
                rc = 0;
                tty_printf(_( "You don't want a passphrase -"
                            " this is probably a *bad* idea!\n\n"));
-               answer = tty_get(_("Do you really want to do this? "));
-               tty_kill_prompt();
-               if( answer_is_yes(answer) )
+               if( cpr_get_answer_is_yes(N_("change_passwd.empty.okay"),
+                              _("Do you really want to do this? ")))
                    changed++;
-               m_free(answer);
                break;
            }
            else { /* okay */
@@ -693,128 +420,1004 @@ change_passphrase( const char *username )
        m_free(dek);
     }
 
-
-    if( changed ) {
-       rc = update_keyblock( &kbpos, keyblock );
-       if( rc ) {
-           log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
-           goto leave;
-       }
-    }
-
   leave:
     m_free( passphrase );
-    release_kbnode( keyblock );
     set_next_passphrase( NULL );
-    return rc;
+    return changed && !rc;
 }
 
 
 /****************
- * Create a signature packet for the given public key certificate
- * and the user id and return it in ret_sig. User signature class SIGCLASS
- * user-id is not used (and may be NULL if sigclass is 0x20)
- * If digest_algo is 0 the function selects an appropriate one.
+ * There are some keys out (due to a bug in gnupg), where the sequence
+ * of the packets is wrong.  This function fixes that.
+ * Returns: true if the keyblock has fixed.
  */
-int
-make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
-                   PKT_user_id *uid, PKT_public_key *subpk,
-                   PKT_secret_key *sk,
-                   int sigclass, int digest_algo,
-                   int (*mksubpkt)(PKT_signature *, void *), void *opaque
-                  )
+static int
+fix_keyblock( KBNODE keyblock )
 {
-    PKT_signature *sig;
-    int rc=0;
-    MD_HANDLE md;
-
-    assert( (sigclass >= 0x10 && sigclass <= 0x13)
-           || sigclass == 0x20 || sigclass == 0x18 );
-    if( !digest_algo ) {
-       switch( sk->pubkey_algo ) {
-         case PUBKEY_ALGO_DSA: digest_algo = DIGEST_ALGO_SHA1; break;
-         case PUBKEY_ALGO_RSA_S:
-         case PUBKEY_ALGO_RSA: digest_algo = DIGEST_ALGO_MD5; break;
-         default:              digest_algo = DIGEST_ALGO_RMD160; break;
-       }
-    }
-    md = md_open( digest_algo, 0 );
-
-    /* hash the public key certificate and the user id */
-    hash_public_key( md, pk );
-    if( sigclass == 0x18 ) { /* subkey binding */
-       hash_public_key( md, subpk );
-    }
-    else if( sigclass != 0x20 ) {
-       if( sk->version >=4 ) {
-           byte buf[5];
-           buf[0] = 0xb4;            /* indicates a userid packet */
-           buf[1] = uid->len >> 24;  /* always use 4 length bytes */
-           buf[2] = uid->len >> 16;
-           buf[3] = uid->len >>  8;
-           buf[4] = uid->len;
-           md_write( md, buf, 5 );
-       }
-       md_write( md, uid->name, uid->len );
-    }
-    /* and make the signature packet */
-    sig = m_alloc_clear( sizeof *sig );
-    sig->version = sk->version;
-    keyid_from_sk( sk, sig->keyid );
-    sig->pubkey_algo = sk->pubkey_algo;
-    sig->digest_algo = digest_algo;
-    sig->timestamp = make_timestamp();
-    sig->sig_class = sigclass;
-    if( sig->version >= 4 )
-       build_sig_subpkt_from_sig( sig );
-
-    if( sig->version >= 4 && mksubpkt )
-       rc = (*mksubpkt)( sig, opaque );
-
-    if( !rc ) {
-       if( sig->version >= 4 )
-           md_putc( md, sig->version );
-       md_putc( md, sig->sig_class );
-       if( sig->version < 4 ) {
-           u32 a = sig->timestamp;
-           md_putc( md, (a >> 24) & 0xff );
-           md_putc( md, (a >> 16) & 0xff );
-           md_putc( md, (a >>  8) & 0xff );
-           md_putc( md,  a        & 0xff );
-       }
-       else {
-           byte buf[6];
-           size_t n;
-
-           md_putc( md, sig->pubkey_algo );
-           md_putc( md, sig->digest_algo );
-           if( sig->hashed_data ) {
-               n = (sig->hashed_data[0] << 8) | sig->hashed_data[1];
-               md_write( md, sig->hashed_data, n+2 );
-               n += 6;
+    KBNODE node, last, subkey;
+    int fixed=0;
+
+    /* locate key signatures of class 0x10..0x13 behind sub key packets */
+    for( subkey=last=NULL, node = keyblock; node;
+                                           last=node, node = node->next ) {
+       switch( node->pkt->pkttype ) {
+         case PKT_PUBLIC_SUBKEY:
+         case PKT_SECRET_SUBKEY:
+           if( !subkey )
+               subkey = last; /* actually it is the one before the subkey */
+           break;
+         case PKT_SIGNATURE:
+           if( subkey ) {
+               PKT_signature *sig = node->pkt->pkt.signature;
+               if( sig->sig_class >= 0x10 && sig->sig_class <= 0x13 ) {
+                   log_info(_(
+                       "moving a key signature to the correct place\n"));
+                   last->next = node->next;
+                   node->next = subkey->next;
+                   subkey->next = node;
+                   node = last;
+                   fixed=1;
+               }
            }
-           else
-               n = 6;
-           /* add some magic */
-           buf[0] = sig->version;
-           buf[1] = 0xff;
-           buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */
-           buf[3] = n >> 16;
-           buf[4] = n >>  8;
-           buf[5] = n;
-           md_write( md, buf, 6 );
-
+           break;
+         default: break;
        }
-       md_final(md);
-
-       rc = complete_sig( sig, sk, md );
     }
 
-    md_close( md );
-    if( rc )
-       free_seckey_enc( sig );
-    else
-       *ret_sig = sig;
-    return rc;
+    return fixed;
+}
+
+/****************
+ * Menu driven key editor
+ *
+ * Note: to keep track of some selection we use node->mark MARKBIT_xxxx.
+ */
+
+void
+keyedit_menu( const char *username, STRLIST locusr )
+{
+    enum cmdids { cmdNONE = 0,
+          cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
+          cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY,
+          cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE,
+          cmdNOP };
+    static struct { const char *name;
+                   enum cmdids id;
+                   int need_sk;
+                   const char *desc;
+                 } cmds[] = {
+       { N_("quit")    , cmdQUIT   , 0, N_("quit this menu") },
+       { N_("q")       , cmdQUIT   , 0, NULL   },
+       { N_("save")    , cmdSAVE   , 0, N_("save and quit") },
+       { N_("help")    , cmdHELP   , 0, N_("show this help") },
+       {    "?"        , cmdHELP   , 0, NULL   },
+       { N_("fpr")     , cmdFPR    , 0, N_("show fingerprint") },
+       { N_("list")    , cmdLIST   , 0, N_("list key and user ids") },
+       { N_("l")       , cmdLIST   , 0, NULL   },
+       { N_("uid")     , cmdSELUID , 0, N_("select user id N") },
+       { N_("key")     , cmdSELKEY , 0, N_("select secondary key N") },
+       { N_("check")   , cmdCHECK  , 0, N_("list signatures") },
+       { N_("c")       , cmdCHECK  , 0, NULL },
+       { N_("sign")    , cmdSIGN   , 0, N_("sign the key") },
+       { N_("s")       , cmdSIGN   , 0, NULL },
+       { N_("debug")   , cmdDEBUG  , 0, NULL },
+       { N_("adduid")  , cmdADDUID , 1, N_("add a user id") },
+       { N_("deluid")  , cmdDELUID , 0, N_("delete user id") },
+       { N_("addkey")  , cmdADDKEY , 1, N_("add a secondary key") },
+       { N_("delkey")  , cmdDELKEY , 0, N_("delete a secondary key") },
+       { N_("expire")  , cmdEXPIRE , 1, N_("change the expire date") },
+       { N_("toggle")  , cmdTOGGLE , 1, N_("toggle between secret "
+                                           "and public key listing") },
+       { N_("t"     )  , cmdTOGGLE , 1, NULL },
+       { N_("pref")    , cmdPREF  , 0, N_("list preferences") },
+       { N_("passwd")  , cmdPASSWD , 1, N_("change the passphrase") },
+       { N_("trust")   , cmdTRUST , 0, N_("change the ownertrust") },
+
+    { NULL, cmdNONE } };
+    enum cmdids cmd;
+    int rc = 0;
+    KBNODE keyblock = NULL;
+    KBPOS keyblockpos;
+    KBNODE sec_keyblock = NULL;
+    KBPOS sec_keyblockpos;
+    KBNODE cur_keyblock;
+    char *answer = NULL;
+    int redisplay = 1;
+    int modified = 0;
+    int sec_modified = 0;
+    int toggle;
+
+
+    if( opt.batch ) {
+       log_error(_("can't do that in batchmode\n"));
+       goto leave;
+    }
+
+    /* first try to locate it as secret key */
+    rc = find_secret_keyblock_byname( &sec_keyblockpos, username );
+    if( !rc ) {
+       rc = read_keyblock( &sec_keyblockpos, &sec_keyblock );
+       if( rc ) {
+           log_error("%s: secret keyblock read problem: %s\n",
+                                           username, g10_errstr(rc));
+           goto leave;
+       }
+       merge_keys_and_selfsig( sec_keyblock );
+       if( fix_keyblock( sec_keyblock ) )
+           sec_modified++;
+    }
+
+    /* and now get the public key */
+    rc = get_keyblock_byname( &keyblock, &keyblockpos, username );
+    if( rc )
+       goto leave;
+    if( fix_keyblock( keyblock ) )
+       modified++;
+
+    if( sec_keyblock ) { /* check that they match */
+       /* FIXME: check that they both match */
+       tty_printf(_("Secret key is available.\n"));
+    }
+
+    toggle = 0;
+    cur_keyblock = keyblock;
+    for(;;) { /* main loop */
+       int i, arg_number;
+       char *p;
+
+       tty_printf("\n");
+       if( redisplay ) {
+           show_key_with_all_names( cur_keyblock, 0, 0, 1, 0 );
+           tty_printf("\n");
+           redisplay = 0;
+       }
+       do {
+           m_free(answer);
+           answer = cpr_get(N_("keyedit.cmd"), _("Command> "));
+           cpr_kill_prompt();
+           trim_spaces(answer);
+       } while( *answer == '#' );
+
+       arg_number = 0;
+       if( !*answer )
+           cmd = cmdLIST;
+       else if( *answer == CONTROL_D )
+           cmd = cmdQUIT;
+       else if( isdigit( *answer ) ) {
+           cmd = cmdSELUID;
+           arg_number = atoi(answer);
+       }
+       else {
+           if( (p=strchr(answer,' ')) ) {
+               *p++ = 0;
+               trim_spaces(answer);
+               trim_spaces(p);
+               arg_number = atoi(p);
+           }
+
+           for(i=0; cmds[i].name; i++ )
+               if( !stricmp( answer, cmds[i].name ) )
+                   break;
+           if( cmds[i].need_sk && !sec_keyblock ) {
+               tty_printf(_("Need the secret key to to this.\n"));
+               cmd = cmdNOP;
+           }
+           else
+               cmd = cmds[i].id;
+       }
+       switch( cmd )  {
+         case cmdHELP:
+           for(i=0; cmds[i].name; i++ ) {
+               if( cmds[i].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) );
+           }
+           break;
+
+         case cmdQUIT:
+           if( !modified && !sec_modified )
+               goto leave;
+           if( !cpr_get_answer_is_yes(N_("keyedit.save.okay"),
+                                       _("Save changes? ")) ) {
+               if( cpr_enabled()
+                   || cpr_get_answer_is_yes(N_("keyedit.cancel.okay"),
+                                            _("Quit without saving? ")) )
+                   goto leave;
+               break;
+           }
+           /* fall thru */
+         case cmdSAVE:
+           if( modified || sec_modified  ) {
+               if( modified ) {
+                   rc = update_keyblock( &keyblockpos, keyblock );
+                   if( rc ) {
+                       log_error(_("update failed: %s\n"), g10_errstr(rc) );
+                       break;
+                   }
+               }
+               if( sec_modified ) {
+                   rc = update_keyblock( &sec_keyblockpos, sec_keyblock );
+                   if( rc ) {
+                       log_error(_("update secret failed: %s\n"),
+                                                           g10_errstr(rc) );
+                       break;
+                   }
+               }
+           }
+           else
+               tty_printf(_("Key not changed so no update needed.\n"));
+           rc = update_trust_record( keyblock, 0, NULL );
+           if( rc )
+               log_error(_("update of trust db failed: %s\n"),
+                           g10_errstr(rc) );
+           goto leave;
+
+         case cmdLIST:
+           redisplay = 1;
+           break;
+
+         case cmdFPR:
+           show_key_and_fingerprint( keyblock );
+           break;
+
+         case cmdSELUID:
+           if( menu_select_uid( cur_keyblock, arg_number ) )
+               redisplay = 1;
+           break;
+
+         case cmdSELKEY:
+           if( menu_select_key( cur_keyblock, arg_number ) )
+               redisplay = 1;
+           break;
+
+         case cmdCHECK:
+           /* we can only do this with the public key becuase the
+            * check functions can't cope with secret keys and it
+            * is questionable whether this would make sense at all */
+           check_all_keysigs( keyblock, count_selected_uids(keyblock) );
+           break;
+
+         case cmdSIGN: /* sign (only the public key) */
+           if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) {
+               if( !cpr_get_answer_is_yes(N_("keyedit.sign_all.okay"),
+                                          _("Really sign all user ids? ")) ) {
+                   tty_printf(_("Hint: Select the user ids to sign\n"));
+                   break;
+               }
+           }
+           sign_uids( keyblock, locusr, &modified );
+           break;
+
+         case cmdDEBUG:
+           dump_kbnode( cur_keyblock );
+           break;
+
+         case cmdTOGGLE:
+           toggle = !toggle;
+           cur_keyblock = toggle? sec_keyblock : keyblock;
+           redisplay = 1;
+           break;
+
+         case cmdADDUID:
+           if( menu_adduid( keyblock, sec_keyblock ) ) {
+               redisplay = 1;
+               sec_modified = modified = 1;
+               /* must update the trustdb already here, so that preferences
+                * get listed correctly */
+               rc = update_trust_record( keyblock, 0, NULL );
+               if( rc ) {
+                   log_error(_("update of trust db failed: %s\n"),
+                               g10_errstr(rc) );
+                   rc = 0;
+               }
+           }
+           break;
+
+         case cmdDELUID: {
+               int n1;
+
+               if( !(n1=count_selected_uids(keyblock)) )
+                   tty_printf(_("You must select at least one user id.\n"));
+               else if( count_uids(keyblock) - n1 < 1 )
+                   tty_printf(_("You can't delete the last user id!\n"));
+               else if( cpr_get_answer_is_yes(
+                           N_("keyedit.remove.uid.okay"),
+                       n1 > 1? _("Really remove all selected user ids? ")
+                             : _("Really remove this user id? ")
+                      ) ) {
+                   menu_deluid( keyblock, sec_keyblock );
+                   redisplay = 1;
+                   modified = 1;
+                   if( sec_keyblock )
+                      sec_modified = 1;
+               }
+           }
+           break;
+
+         case cmdADDKEY:
+           if( generate_subkeypair( keyblock, sec_keyblock ) ) {
+               redisplay = 1;
+               sec_modified = modified = 1;
+           }
+           break;
+
+
+         case cmdDELKEY: {
+               int n1;
+
+               if( !(n1=count_selected_keys( keyblock )) )
+                   tty_printf(_("You must select at least one key.\n"));
+               else if( sec_keyblock && !cpr_get_answer_is_yes(
+                           N_("keyedit.remove.subkey.okay"),
+                      n1 > 1?
+                       _("Do you really want to delete the selected keys? "):
+                       _("Do you really want to delete this key? ")
+                      ))
+                   ;
+               else {
+                   menu_delkey( keyblock, sec_keyblock );
+                   redisplay = 1;
+                   modified = 1;
+                   if( sec_keyblock )
+                      sec_modified = 1;
+               }
+           }
+           break;
+
+         case cmdEXPIRE:
+           if( menu_expire( keyblock, sec_keyblock ) ) {
+               merge_keys_and_selfsig( sec_keyblock );
+               merge_keys_and_selfsig( keyblock );
+               sec_modified = 1;
+               modified = 1;
+               redisplay = 1;
+           }
+           break;
+
+         case cmdPASSWD:
+           if( change_passphrase( sec_keyblock ) )
+               sec_modified = 1;
+           break;
+
+         case cmdTRUST:
+           show_key_with_all_names( keyblock, 0, 0, 1, 0 );
+           tty_printf("\n");
+           if( edit_ownertrust( find_kbnode( keyblock,
+                     PKT_PUBLIC_KEY )->pkt->pkt.public_key->local_id, 1 ) )
+               redisplay = 1;
+           /* we don't need to set modified here, as the trustvalues
+            * are updated immediately */
+           break;
+
+         case cmdPREF:
+           show_key_with_all_names( keyblock, 0, 0, 0, 1 );
+           break;
+
+         case cmdNOP:
+           break;
+
+         default:
+           tty_printf("\n");
+           tty_printf(_("Invalid command  (try \"help\")\n"));
+           break;
+       }
+    } /* end main loop */
+
+  leave:
+    release_kbnode( keyblock );
+    release_kbnode( sec_keyblock );
+    m_free(answer);
+}
+
+
+/****************
+ * show preferences of a public keyblock.
+ */
+static void
+show_prefs( KBNODE keyblock, PKT_user_id *uid )
+{
+    KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
+    PKT_public_key *pk;
+    byte *p;
+    int i;
+    size_t n;
+    byte namehash[20];
+
+    if( !node )
+       return; /* is a secret keyblock */
+    pk = node->pkt->pkt.public_key;
+    if( !pk->local_id ) {
+       log_error("oops: no LID\n");
+       return;
+    }
+
+    rmd160_hash_buffer( namehash, uid->name, uid->len );
+
+    p = get_pref_data( pk->local_id, namehash, &n );
+    if( !p )
+       return;
+
+    tty_printf("    ");
+    for(i=0; i < n; i+=2 ) {
+       if( p[i] )
+           tty_printf( " %c%d", p[i] == PREFTYPE_SYM    ? 'S' :
+                                p[i] == PREFTYPE_HASH   ? 'H' :
+                                p[i] == PREFTYPE_COMPR  ? 'Z' : '?', p[i+1]);
+    }
+    tty_printf("\n");
+
+    m_free(p);
+}
+
+
+/****************
+ * Display the key a the user ids, if only_marked is true, do only
+ * so for user ids with mark A flag set and dont display the index number
+ */
+static void
+show_key_with_all_names( KBNODE keyblock, int only_marked,
+                        int with_fpr, int with_subkeys, int with_prefs )
+{
+    KBNODE node;
+    int i;
+
+    /* the keys */
+    for( node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_KEY
+           || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) {
+           PKT_public_key *pk = node->pkt->pkt.public_key;
+           int otrust=0, trust=0;
+
+           if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+               /* do it here, so that debug messages don't clutter the
+                * output */
+               trust = query_trust_info(pk);
+               otrust = get_ownertrust_info( pk->local_id );
+           }
+
+           tty_printf("%s%c %4u%c/%08lX  created: %s expires: %s",
+                         node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
+                         (node->flag & NODFLG_SELKEY)? '*':' ',
+                         nbits_from_pk( pk ),
+                         pubkey_letter( pk->pubkey_algo ),
+                         (ulong)keyid_from_pk(pk,NULL),
+                         datestr_from_pk(pk),
+                         expirestr_from_pk(pk) );
+           if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+               tty_printf(" trust: %c/%c", otrust, trust );
+               if( with_fpr  ) {
+                   tty_printf("\n");
+                   show_fingerprint( pk );
+               }
+           }
+           tty_printf("\n");
+       }
+       else if( node->pkt->pkttype == PKT_SECRET_KEY
+           || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) ) {
+           PKT_secret_key *sk = node->pkt->pkt.secret_key;
+           tty_printf("%s%c %4u%c/%08lX  created: %s expires: %s\n",
+                         node->pkt->pkttype == PKT_SECRET_KEY? "sec":"sbb",
+                         (node->flag & NODFLG_SELKEY)? '*':' ',
+                         nbits_from_sk( sk ),
+                         pubkey_letter( sk->pubkey_algo ),
+                         (ulong)keyid_from_sk(sk,NULL),
+                         datestr_from_sk(sk),
+                         expirestr_from_sk(sk) );
+       }
+    }
+    /* 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( !only_marked || (only_marked && (node->flag & NODFLG_MARK_A))){
+               if( only_marked )
+                  tty_printf("     ");
+               else if( node->flag & NODFLG_SELUID )
+                  tty_printf("(%d)* ", i);
+               else
+                  tty_printf("(%d)  ", i);
+               tty_print_string( uid->name, uid->len );
+               tty_printf("\n");
+               if( with_prefs )
+                   show_prefs( keyblock, uid );
+           }
+       }
+    }
+}
+
+static void
+show_key_and_fingerprint( KBNODE keyblock )
+{
+    KBNODE node;
+    PKT_public_key *pk = NULL;
+
+    for( node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+           pk = node->pkt->pkt.public_key;
+           tty_printf("pub  %4u%c/%08lX %s ",
+                         nbits_from_pk( pk ),
+                         pubkey_letter( pk->pubkey_algo ),
+                         (ulong)keyid_from_pk(pk,NULL),
+                         datestr_from_pk(pk) );
+       }
+       else if( node->pkt->pkttype == PKT_USER_ID ) {
+           PKT_user_id *uid = node->pkt->pkt.user_id;
+           tty_print_string( uid->name, uid->len );
+           break;
+       }
+    }
+    tty_printf("\n");
+    if( pk )
+       show_fingerprint( pk );
+}
+
+
+static void
+show_fingerprint( PKT_public_key *pk )
+{
+    byte array[MAX_FINGERPRINT_LEN], *p;
+    size_t i, n;
+
+    fingerprint_from_pk( pk, array, &n );
+    p = array;
+    tty_printf("             Fingerprint:");
+    if( n == 20 ) {
+       for(i=0; i < n ; i++, i++, p += 2 ) {
+           if( i == 10 )
+               tty_printf(" ");
+           tty_printf(" %02X%02X", *p, p[1] );
+       }
+    }
+    else {
+       for(i=0; i < n ; i++, p++ ) {
+           if( i && !(i%8) )
+               tty_printf(" ");
+           tty_printf(" %02X", *p );
+       }
+    }
+    tty_printf("\n");
+}
+
+
+/****************
+ * Ask for a new user id , do the selfsignature and put it into
+ * both keyblocks.
+ * Return true if there is a new user id
+ */
+static int
+menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+    PKT_user_id *uid;
+    PKT_public_key *pk=NULL;
+    PKT_secret_key *sk=NULL;
+    PKT_signature *sig=NULL;
+    PACKET *pkt;
+    KBNODE node;
+    KBNODE pub_where=NULL, sec_where=NULL;
+    int rc;
+
+    uid = generate_user_id();
+    if( !uid )
+       return 0;
+
+    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;
+    for( node = sec_keyblock; node; sec_where = node, node = node->next ) {
+       if( node->pkt->pkttype == PKT_SECRET_KEY )
+           sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+       else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
+           break;
+    }
+    if( !node ) /* no subkey */
+       sec_where = NULL;
+    assert(pk && sk );
+
+    rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0,
+                            keygen_add_std_prefs, sk );
+    free_secret_key( sk );
+    if( rc ) {
+       log_error("signing failed: %s\n", g10_errstr(rc) );
+       free_user_id(uid);
+       return 0;
+    }
+
+    /* insert/append to secret keyblock */
+    pkt = m_alloc_clear( sizeof *pkt );
+    pkt->pkttype = PKT_USER_ID;
+    pkt->pkt.user_id = copy_user_id(NULL, uid);
+    node = new_kbnode(pkt);
+    if( sec_where )
+       insert_kbnode( sec_where, node, 0 );
+    else
+       add_kbnode( sec_keyblock, node );
+    pkt = m_alloc_clear( sizeof *pkt );
+    pkt->pkttype = PKT_SIGNATURE;
+    pkt->pkt.signature = copy_signature(NULL, sig);
+    if( sec_where )
+       insert_kbnode( node, new_kbnode(pkt), 0 );
+    else
+       add_kbnode( sec_keyblock, new_kbnode(pkt) );
+    /* insert/append to public keyblock */
+    pkt = m_alloc_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 = m_alloc_clear( sizeof *pkt );
+    pkt->pkttype = PKT_SIGNATURE;
+    pkt->pkt.signature = copy_signature(NULL, sig);
+    if( pub_where )
+       insert_kbnode( node, new_kbnode(pkt), 0 );
+    else
+       add_kbnode( pub_keyblock, new_kbnode(pkt) );
+    return 1;
+}
+
+
+/****************
+ * Remove all selceted userids from the keyrings
+ */
+static void
+menu_deluid( KBNODE pub_keyblock, KBNODE sec_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 ) {
+               delete_kbnode( node );
+               if( sec_keyblock ) {
+                   KBNODE snode;
+                   int s_selected = 0;
+                   PKT_user_id *uid = node->pkt->pkt.user_id;
+                   for( snode = sec_keyblock; snode; snode = snode->next ) {
+                       if( snode->pkt->pkttype == PKT_USER_ID ) {
+                           PKT_user_id *suid = snode->pkt->pkt.user_id;
+
+                           s_selected =
+                               (uid->len == suid->len
+                                && !memcmp( uid->name, suid->name, uid->len));
+                           if( s_selected )
+                               delete_kbnode( snode );
+                       }
+                       else if( s_selected
+                                && snode->pkt->pkttype == PKT_SIGNATURE )
+                           delete_kbnode( snode );
+                       else if( snode->pkt->pkttype == PKT_SECRET_SUBKEY )
+                           s_selected = 0;
+                   }
+               }
+           }
+       }
+       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 );
+    if( sec_keyblock )
+       commit_kbnode( &sec_keyblock );
+}
+
+
+/****************
+ * Remove some of the secondary keys
+ */
+static void
+menu_delkey( KBNODE pub_keyblock, KBNODE sec_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 );
+               if( sec_keyblock ) {
+                   KBNODE snode;
+                   int s_selected = 0;
+                   u32 ki[2];
+
+                   keyid_from_pk( node->pkt->pkt.public_key, ki );
+                   for( snode = sec_keyblock; snode; snode = snode->next ) {
+                       if( snode->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+                           u32 ki2[2];
+
+                           keyid_from_sk( snode->pkt->pkt.secret_key, ki2 );
+                           s_selected = (ki[0] == ki2[0] && ki[1] == ki2[1]);
+                           if( s_selected )
+                               delete_kbnode( snode );
+                       }
+                       else if( s_selected
+                                && snode->pkt->pkttype == PKT_SIGNATURE )
+                           delete_kbnode( snode );
+                       else
+                           s_selected = 0;
+                   }
+               }
+           }
+       }
+       else if( selected && node->pkt->pkttype == PKT_SIGNATURE )
+           delete_kbnode( node );
+       else
+           selected = 0;
+    }
+    commit_kbnode( &pub_keyblock );
+    if( sec_keyblock )
+       commit_kbnode( &sec_keyblock );
+}
+
+
+
+static int
+menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+    int n1, rc;
+    u32 expiredate;
+    int mainkey=0;
+    PKT_secret_key *sk;    /* copy of the main sk */
+    PKT_public_key *main_pk, *sub_pk;
+    PKT_user_id *uid;
+    KBNODE node;
+    u32 keyid[2];
+
+    if( count_selected_keys( sec_keyblock ) ) {
+       tty_printf(_("Please remove selections from the secret keys.\n"));
+       return 0;
+    }
+
+    n1 = count_selected_keys( pub_keyblock );
+    if( n1 > 1 ) {
+       tty_printf(_("Please select at most one secondary key.\n"));
+       return 0;
+    }
+    else if( n1 )
+       tty_printf(_("Changing exiration time for a secondary key.\n"));
+    else {
+       tty_printf(_("Changing exiration time for the primary key.\n"));
+       mainkey=1;
+    }
+
+    expiredate = ask_expiredate();
+    /* fixme: check that expiredate is > key creation date */
+
+    /* get the secret key , make a copy and set the expiration time into
+     * that key (because keygen_add-key-expire expects it there)
+     */
+    node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+    sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+    sk->expiredate = expiredate;
+
+    /* Now we can actually change the self signature(s) */
+    main_pk = sub_pk = NULL;
+    uid = NULL;
+    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 );
+       }
+       else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                && (node->flag & NODFLG_SELKEY ) )
+           sub_pk = node->pkt->pkt.public_key;
+       else if( node->pkt->pkttype == PKT_USER_ID )
+           uid = node->pkt->pkt.user_id;
+       else if( main_pk && node->pkt->pkttype == PKT_SIGNATURE ) {
+           PKT_signature *sig = node->pkt->pkt.signature;
+           if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+               && (    (mainkey && uid && (sig->sig_class&~3) == 0x10)
+                    || (!mainkey && sig->sig_class == 0x18)  ) ) {
+               /* this is a selfsignature which should be replaced */
+               PKT_signature *newsig;
+               PACKET *newpkt;
+               KBNODE sn;
+
+               /* find the corresponding secret self-signature */
+               for( sn=sec_keyblock; sn; sn = sn->next ) {
+                   if( sn->pkt->pkttype == PKT_SIGNATURE
+                       && !cmp_signatures( sn->pkt->pkt.signature, sig ) )
+                       break;
+               }
+               if( !sn )
+                   log_info(_("No corresponding signature in secret ring\n"));
+
+               /* create new self signature */
+               if( mainkey )
+                   rc = make_keysig_packet( &newsig, main_pk, uid, NULL,
+                                            sk, 0x13, 0,
+                                            keygen_add_std_prefs, sk );
+               else
+                   rc = make_keysig_packet( &newsig, main_pk, NULL, sub_pk,
+                                            sk, 0x18, 0,
+                                            keygen_add_key_expire, sk );
+               if( rc ) {
+                   log_error("make_keysig_packet failed: %s\n",
+                                                   g10_errstr(rc));
+                   free_secret_key( sk );
+                   return 0;
+               }
+               /* replace the packet */
+               newpkt = m_alloc_clear( sizeof *newpkt );
+               newpkt->pkttype = PKT_SIGNATURE;
+               newpkt->pkt.signature = newsig;
+               free_packet( node->pkt );
+               m_free( node->pkt );
+               node->pkt = newpkt;
+               if( sn ) {
+                   newpkt = m_alloc_clear( sizeof *newpkt );
+                   newpkt->pkttype = PKT_SIGNATURE;
+                   newpkt->pkt.signature = copy_signature( NULL, newsig );
+                   free_packet( sn->pkt );
+                   m_free( sn->pkt );
+                   sn->pkt = newpkt;
+               }
+           }
+       }
+    }
+
+    free_secret_key( sk );
+    return 1;
+}
+
+
+/****************
+ * Select one user id or remove all selection if index is 0.
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_uid( KBNODE keyblock, int index )
+{
+    KBNODE node;
+    int i;
+
+    /* first check that the index is valid */
+    if( index ) {
+       for( i=0, node = keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_USER_ID ) {
+               if( ++i == index )
+                   break;
+           }
+       }
+       if( !node ) {
+           tty_printf(_("No user id with index %d\n"), index );
+           return 0;
+       }
+    }
+    else { /* reset all */
+       for( i=0, node = keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_USER_ID )
+               node->flag &= ~NODFLG_SELUID;
+       }
+       return 1;
+    }
+    /* and toggle the new index */
+    for( i=0, node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_USER_ID ) {
+           if( ++i == index )
+               if( (node->flag & NODFLG_SELUID) )
+                   node->flag &= ~NODFLG_SELUID;
+               else
+                   node->flag |= NODFLG_SELUID;
+       }
+    }
+
+    return 1;
+}
+
+/****************
+ * Select secondary keys
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_key( KBNODE keyblock, int index )
+{
+    KBNODE node;
+    int i;
+
+    /* first check that the index is valid */
+    if( index ) {
+       for( i=0, node = keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+               || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+               if( ++i == index )
+                   break;
+           }
+       }
+       if( !node ) {
+           tty_printf(_("No secondary key with index %d\n"), index );
+           return 0;
+       }
+    }
+    else { /* reset all */
+       for( i=0, 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;
+    }
+    /* and set the new index */
+    for( i=0, node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+           || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+           if( ++i == index )
+               if( (node->flag & NODFLG_SELKEY) )
+                   node->flag &= ~NODFLG_SELKEY;
+               else
+                   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);
 }