Fix for bug 537
[gnupg.git] / g10 / keyedit.c
index 5224723..71ad9f0 100644 (file)
@@ -1,6 +1,6 @@
 /* keyedit.c - keyedit stuff
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- *               2005 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <assert.h>
 #include <ctype.h>
 #ifdef HAVE_LIBREADLINE
-#include <stdio.h>
+#define GNUPG_LIBREADLINE_H_INCLUDED
 #include <readline/readline.h>
 #endif
+
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include "memory.h"
 #include "photoid.h"
 #include "util.h"
 #include "main.h"
 #include "keyserver-internal.h"
 
 static void show_prefs( PKT_user_id *uid, PKT_signature *selfsig, int verbose);
+static void show_names(KBNODE keyblock,PKT_public_key *pk,
+                      unsigned int flag,int with_prefs);
 static void show_key_with_all_names( KBNODE keyblock, int only_marked,
            int with_revoker, int with_fpr, int with_subkeys, int with_prefs );
 static void show_key_and_fingerprint( KBNODE keyblock );
-static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock, int photo );
+static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock,
+                       int photo, const char *photo_name );
 static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_delsig( KBNODE pub_keyblock );
-static int menu_clean_sigs_from_uids(KBNODE keyblock);
-static int menu_clean_uids_from_key(KBNODE keyblock);
+static int menu_clean(KBNODE keyblock,int self_only);
 static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_addrevoker( KBNODE pub_keyblock,
                            KBNODE sec_keyblock, int sensitive );
 static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock);
 static int menu_set_primary_uid( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_set_preferences( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_set_keyserver_url (const char *url,
                                   KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_set_notation(const char *string,
+                            KBNODE pub_keyblock,KBNODE sec_keyblock);
 static int menu_select_uid( KBNODE keyblock, int idx );
 static int menu_select_uid_namehash( KBNODE keyblock, const char *namehash );
 static int menu_select_key( KBNODE keyblock, int idx );
@@ -501,7 +507,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
  * if some user_ids are marked those will be signed.
  */
 static int
-sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
+sign_uids( KBNODE keyblock, strlist_t locusr, int *ret_modified,
           int local, int nonrevocable, int trust, int interactive )
 {
     int rc = 0;
@@ -543,7 +549,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
        byte trust_depth=0,trust_value=0;
 
        if(local || nonrevocable || trust ||
-          opt.cert_policy_url || opt.cert_notation_data)
+          opt.cert_policy_url || opt.cert_notations)
          force_v4=1;
 
        /* we have to use a copy of the sk, because make_keysig_packet
@@ -1335,9 +1341,10 @@ enum cmdids
     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, cmdCLEAN, cmdNOP
+    cmdEXPIRE, cmdBACKSIGN, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF,
+    cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
+    cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN,
+    cmdMINIMIZE, cmdNOP
   };
 
 static struct
@@ -1360,6 +1367,8 @@ static struct
     { "key"     , cmdSELKEY    , 0, N_("select subkey N") },
     { "check"   , cmdCHECK     , 0, N_("check signatures") },
     { "c"       , cmdCHECK     , 0, NULL },
+    { "cross-certify", cmdBACKSIGN  , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+    { "backsign", cmdBACKSIGN  , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
     { "sign"    , cmdSIGN      , KEYEDIT_NOT_SK|KEYEDIT_TAIL_MATCH,
       N_("sign selected user IDs [* see below for related commands]") },
     { "s"       , cmdSIGN      , KEYEDIT_NOT_SK, NULL },
@@ -1417,7 +1426,9 @@ static struct
     { "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")},
+      N_("set the preferred keyserver URL for the selected user IDs")},
+    { "notation", cmdNOTATION  , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("set a notation for the selected user IDs")},
     { "passwd"  , cmdPASSWD    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
       N_("change the passphrase") },
     /* Alias */
@@ -1437,11 +1448,12 @@ static struct
     { "disable" , cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key") },
     { "showphoto",cmdSHOWPHOTO , 0, N_("show selected photo IDs") },
     { "clean",    cmdCLEAN     , KEYEDIT_NOT_SK,
-      N_("clean unusable parts from key") },
+      N_("compact unusable user IDs and remove unusable signatures from key")},
+    { "minimize", cmdMINIMIZE  , KEYEDIT_NOT_SK,
+      N_("compact unusable user IDs and remove all signatures from key") },
     { NULL, cmdNONE, 0, NULL }
   };
 
-
 #ifdef HAVE_LIBREADLINE
 
 /* These two functions are used by readline for command completion. */
@@ -1489,8 +1501,8 @@ keyedit_completion(const char *text, int start, int end)
 
 
 void
-keyedit_menu( const char *username, STRLIST locusr,
-             STRLIST commands, int quiet, int seckey_check )
+keyedit_menu( const char *username, strlist_t locusr,
+             strlist_t commands, int quiet, int seckey_check )
 {
     enum cmdids cmd = 0;
     int rc = 0;
@@ -1788,7 +1800,7 @@ keyedit_menu( const char *username, STRLIST locusr,
            /* fall through */
 
          case cmdADDUID:
-           if( menu_adduid( keyblock, sec_keyblock, photo ) )
+           if( menu_adduid( keyblock, sec_keyblock, photo, arg_string ) )
              {
                update_trust = 1;
                redisplay = 1;
@@ -2048,6 +2060,15 @@ keyedit_menu( const char *username, STRLIST locusr,
              }
            break;
 
+         case cmdBACKSIGN:
+           if(menu_backsign(keyblock,sec_keyblock))
+             {
+               sec_modified = 1;
+               modified = 1;
+               redisplay = 1;
+             }
+           break;
+
          case cmdPRIMARY:
            if( menu_set_primary_uid ( keyblock, sec_keyblock ) ) {
                merge_keys_and_selfsig( keyblock );
@@ -2082,11 +2103,21 @@ keyedit_menu( const char *username, STRLIST locusr,
            break;
 
          case cmdPREF:
-           show_key_with_all_names( keyblock, 0, 0, 0, 0, 1 );
+           {
+             int count=count_selected_uids(keyblock);
+             assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+             show_names(keyblock,keyblock->pkt->pkt.public_key,
+                        count?NODFLG_SELUID:0,1);
+           }
            break;
 
          case cmdSHOWPREF:
-           show_key_with_all_names( keyblock, 0, 0, 0, 0, 2 );
+           {
+             int count=count_selected_uids(keyblock);
+             assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+             show_names(keyblock,keyblock->pkt->pkt.public_key,
+                        count?NODFLG_SELUID:0,2);
+           }
            break;
 
           case cmdSETPREF:
@@ -2126,6 +2157,16 @@ keyedit_menu( const char *username, STRLIST locusr,
              }
            break;
 
+         case cmdNOTATION:
+           if( menu_set_notation ( *arg_string?arg_string:NULL,
+                                   keyblock, sec_keyblock ) )
+             {
+               merge_keys_and_selfsig( keyblock );
+               modified = 1;
+               redisplay = 1;
+             }
+           break;
+
          case cmdNOP:
            break;
 
@@ -2149,26 +2190,11 @@ keyedit_menu( const char *username, STRLIST locusr,
            break;
 
          case cmdCLEAN:
-           {
-             if(*arg_string)
-               {
-                 if(ascii_strcasecmp(arg_string,"sigs")==0
-                    || ascii_strcasecmp(arg_string,"signatures")==0
-                    || ascii_strcasecmp(arg_string,"certs")==0
-                    || ascii_strcasecmp(arg_string,"certificates")==0)
-                   modified=menu_clean_sigs_from_uids(keyblock);
-                 else if(ascii_strcasecmp(arg_string,"uids")==0)
-                   redisplay=modified=menu_clean_uids_from_key(keyblock);
-                 else
-                   tty_printf("Unable to clean `%s'\n",arg_string);
-               }
-             else
-               {
-                 modified=menu_clean_sigs_from_uids(keyblock);
-                 modified+=menu_clean_uids_from_key(keyblock);
-                 redisplay=modified;
-               }
-           }
+           redisplay=modified=menu_clean(keyblock,0);
+           break;
+
+         case cmdMINIMIZE:
+           redisplay=modified=menu_clean(keyblock,1);
            break;
 
          case cmdQUIT:
@@ -2228,8 +2254,36 @@ keyedit_menu( const char *username, STRLIST locusr,
     xfree(answer);
 }
 
+static void
+tty_print_notations(int indent,PKT_signature *sig)
+{
+  int first=1;
+  struct notation *notation,*nd;
+
+  if(indent<0)
+    {
+      first=0;
+      indent=-indent;
+    }
+
+  notation=sig_to_notation(sig);
+
+  for(nd=notation;nd;nd=nd->next)
+    {
+      if(!first)
+       tty_printf("%*s",indent,"");
+      else
+       first=0;
+
+      tty_print_utf8_string(nd->name,strlen(nd->name));
+      tty_printf("=");
+      tty_print_utf8_string(nd->value,strlen(nd->value));
+      tty_printf("\n");
+    }
+
+  free_notation(notation);
+}
 
-\f
 /****************
  * show preferences of a public keyblock.
  */
@@ -2257,7 +2311,7 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
        tty_printf (_("Cipher: "));
         for(i=any=0; prefs[i].type; i++ ) {
             if( prefs[i].type == PREFTYPE_SYM ) {
-                const char *s = cipher_algo_to_string (prefs[i].value);
+                const char *s = gcry_cipher_algo_name (prefs[i].value);
                 
                 if (any)
                     tty_printf (", ");
@@ -2274,13 +2328,13 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
         if (!des_seen) {
             if (any)
                 tty_printf (", ");
-            tty_printf ("%s",cipher_algo_to_string(CIPHER_ALGO_3DES));
+            tty_printf ("%s", gcry_cipher_algo_name (CIPHER_ALGO_3DES));
         }
         tty_printf ("\n     ");
        tty_printf (_("Digest: "));
         for(i=any=0; prefs[i].type; i++ ) {
             if( prefs[i].type == PREFTYPE_HASH ) {
-                const char *s = digest_algo_to_string (prefs[i].value);
+                const char *s = gcry_md_algo_name (prefs[i].value);
                 
                 if (any)
                     tty_printf (", ");
@@ -2297,7 +2351,7 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
         if (!sha1_seen) {
             if (any)
                 tty_printf (", ");
-            tty_printf ("%s",digest_algo_to_string(DIGEST_ALGO_SHA1));
+            tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1));
         }
         tty_printf ("\n     ");
        tty_printf (_("Compression: "));
@@ -2326,17 +2380,17 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
            }
            tty_printf ("%s",compress_algo_to_string(COMPRESS_ALGO_NONE));
         }
-       if(uid->mdc_feature || !uid->ks_modify)
+       if(uid->flags.mdc || !uid->flags.ks_modify)
          {
            tty_printf ("\n     ");
            tty_printf (_("Features: "));
            any=0;
-           if(uid->mdc_feature)
+           if(uid->flags.mdc)
              {
                tty_printf ("MDC");
                any=1;
              }
-           if(!uid->ks_modify)
+           if(!uid->flags.ks_modify)
              {
                if(any)
                  tty_printf (", ");
@@ -2359,6 +2413,13 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
                tty_print_utf8_string(pref_ks,pref_ks_len);
                tty_printf("\n");
              }
+
+           if(selfsig->flags.notation)
+             {
+               tty_printf ("     ");
+               tty_printf(_("Notations: "));
+               tty_print_notations(5+strlen(_("Notations: ")),selfsig);
+             }
          }
     }
     else {
@@ -2369,9 +2430,9 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
                                  prefs[i].type == PREFTYPE_ZIP ? 'Z':'?',
                                  prefs[i].value);
         }
-        if (uid->mdc_feature)
+        if (uid->flags.mdc)
             tty_printf (" [mdc]");
-        if (!uid->ks_modify)
+        if (!uid->flags.ks_modify)
             tty_printf (" [no-ks-modify]");
         tty_printf("\n");
     }
@@ -2433,24 +2494,7 @@ show_key_with_all_names_colon (KBNODE keyblock)
           putchar('\n');
           
           print_fingerprint (pk, NULL, 0);
-
-          /* print the revoker record */
-          if( !pk->revkey && pk->numrevkeys )
-            BUG();
-          else
-            {
-              for (i=0; i < pk->numrevkeys; i++)
-                {
-                  byte *p;
-
-                  printf ("rvk:::%d::::::", pk->revkey[i].algid);
-                  p = pk->revkey[i].fpr;
-                  for (j=0; j < 20; j++, p++ )
-                    printf ("%02X", *p);
-                  printf (":%02x%s:\n", pk->revkey[i].class,
-                          (pk->revkey[i].class&0x40)?"s":"");
-                }
-            }
+         print_revokers(pk);
         }
     }
   
@@ -2510,9 +2554,9 @@ show_key_with_all_names_colon (KBNODE keyblock)
                             prefs[j].type == PREFTYPE_ZIP ? 'Z':'?',
                             prefs[j].value);
                   } 
-                if (uid->mdc_feature)
+                if (uid->flags.mdc)
                   printf (",mdc");
-                if (!uid->ks_modify)
+                if (!uid->flags.ks_modify)
                   printf (",no-ks-modify");
               } 
             putchar (':');
@@ -2534,6 +2578,63 @@ show_key_with_all_names_colon (KBNODE keyblock)
       }
 }
 
+static void
+show_names(KBNODE keyblock,PKT_public_key *pk,unsigned int flag,int with_prefs)
+{
+  KBNODE node;
+  int i=0;
+
+  for( node = keyblock; node; node = node->next )
+    {
+      if( node->pkt->pkttype == PKT_USER_ID
+         && !is_deleted_kbnode(node))
+       {
+         PKT_user_id *uid = node->pkt->pkt.user_id;
+         ++i;
+         if(!flag || (flag && (node->flag & flag)))
+           {
+             if(!(flag&NODFLG_MARK_A) && pk)
+               tty_printf("%s ",uid_trust_string_fixed(pk,uid));
+
+             if( flag & NODFLG_MARK_A )
+               tty_printf("     ");
+             else if( node->flag & NODFLG_SELUID )
+               tty_printf("(%d)* ", i);
+             else if( uid->is_primary )
+               tty_printf("(%d). ", i);
+             else
+               tty_printf("(%d)  ", i);
+             tty_print_utf8_string( uid->name, uid->len );
+             tty_printf("\n");
+             if(with_prefs && pk)
+               {
+                 if(pk->version>3 || uid->selfsigversion>3)
+                   {
+                     PKT_signature *selfsig=NULL;
+                     KBNODE signode;
+
+                     for(signode=node->next;
+                         signode && signode->pkt->pkttype==PKT_SIGNATURE;
+                         signode=signode->next)
+                       {
+                         if(signode->pkt->pkt.signature->
+                            flags.chosen_selfsig)
+                           {
+                             selfsig=signode->pkt->pkt.signature;
+                             break;
+                           }
+                       }
+
+                     show_prefs (uid, selfsig, with_prefs == 2);
+                   }
+                 else
+                   tty_printf(_("There are no preferences on a"
+                                " PGP 2.x-style user ID.\n"));
+               }
+           }
+       }
+    }
+}
 
 /****************
  * Display the key a the user ids, if only_marked is true, do only
@@ -2585,7 +2686,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
            if(pk->is_revoked)
              {
                char *user=get_user_id_string_native(pk->revoked.keyid);
-               const char *algo=pubkey_algo_to_string(pk->revoked.algo);
+               const char *algo = gcry_pk_algo_name (pk->revoked.algo);
                tty_printf(_("This key was revoked on %s by %s key %s\n"),
                           revokestr_from_pk(pk),algo?algo:"?",user);
                xfree(user);
@@ -2600,9 +2701,9 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
                    {
                      u32 r_keyid[2];
                      char *user;
-                     const char *algo=
-                       pubkey_algo_to_string(pk->revkey[i].algid);
+                     const char *algo;
 
+                      algo = gcry_pk_algo_name (pk->revkey[i].algid);
                      keyid_from_fingerprint(pk->revkey[i].fpr,
                                             MAX_FINGERPRINT_LEN,r_keyid);
 
@@ -2712,60 +2813,8 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
               }
          }
     }
-    
-    /* the user ids */
 
-    i = 0;
-    for( node = keyblock; node; node = node->next )
-      {
-       if( node->pkt->pkttype == PKT_USER_ID
-           && !is_deleted_kbnode(node))
-         {
-           PKT_user_id *uid = node->pkt->pkt.user_id;
-           ++i;
-           if( !only_marked || (only_marked && (node->flag & NODFLG_MARK_A)))
-             {
-               if(!only_marked && primary)
-                 tty_printf("%s ",uid_trust_string_fixed(primary,uid));
-
-               if( only_marked )
-                 tty_printf("     ");
-               else if( node->flag & NODFLG_SELUID )
-                 tty_printf("(%d)* ", i);
-               else if( uid->is_primary )
-                 tty_printf("(%d). ", i);
-               else
-                 tty_printf("(%d)  ", i);
-               tty_print_utf8_string( uid->name, uid->len );
-               tty_printf("\n");
-               if( with_prefs )
-                 {
-                   if(pk_version>3 || uid->selfsigversion>3)
-                     {
-                       PKT_signature *selfsig=NULL;
-                       KBNODE signode;
-
-                       for(signode=node->next;
-                           signode && signode->pkt->pkttype==PKT_SIGNATURE;
-                           signode=signode->next)
-                         {
-                           if(signode->pkt->pkt.signature->
-                              flags.chosen_selfsig)
-                             {
-                               selfsig=signode->pkt->pkt.signature;
-                               break;
-                             }
-                         }
-
-                       show_prefs (uid, selfsig, with_prefs == 2);
-                     }
-                   else
-                     tty_printf(_("There are no preferences on a"
-                                  " PGP 2.x-style user ID.\n"));
-                 }
-             }
-         }
-      }
+    show_names(keyblock,primary,only_marked?NODFLG_MARK_A:0,with_prefs);
 
     if (do_warn)
         tty_printf (_("Please note that the shown key validity"
@@ -2774,7 +2823,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
 }
 
 
-/* Display basic key information.  This fucntion is suitable to show
+/* Display basic key information.  This function is suitable to show
    information on the key without any dependencies on the trustdb or
    any other internal GnuPG stuff.  KEYBLOCK may either be a public or
    a secret key.*/
@@ -2910,7 +2959,8 @@ no_primary_warning(KBNODE keyblock)
  * Return true if there is a new user id
  */
 static int
-menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
+menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock,
+            int photo, const char *photo_name)
 {
     PKT_user_id *uid;
     PKT_public_key *pk=NULL;
@@ -2976,7 +3026,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
            }
        }
 
-      uid = generate_photo_id(pk);
+      uid = generate_photo_id(pk,photo_name);
     } else
       uid = generate_user_id();
     if( !uid )
@@ -3154,59 +3204,27 @@ menu_delsig( KBNODE pub_keyblock )
 }
 
 static int
-menu_clean_sigs_from_uids(KBNODE keyblock)
+menu_clean(KBNODE keyblock,int self_only)
 {
   KBNODE uidnode;
-  int modified=0;
-  int select_all=!count_selected_uids(keyblock);
+  int modified=0,select_all=!count_selected_uids(keyblock);
 
-  for(uidnode=keyblock->next;uidnode;uidnode=uidnode->next)
+  for(uidnode=keyblock->next;
+      uidnode && uidnode->pkt->pkttype!=PKT_PUBLIC_SUBKEY;
+      uidnode=uidnode->next)
     {
       if(uidnode->pkt->pkttype==PKT_USER_ID
         && (uidnode->flag&NODFLG_SELUID || select_all))
        {
-         int deleted;
+         int uids=0,sigs=0;
          char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
                                    uidnode->pkt->pkt.user_id->len,
                                    0);
-         deleted=clean_sigs_from_uid(keyblock,uidnode,opt.verbose);
-         if(deleted)
-           {
-             tty_printf(deleted==1?
-                        "User ID \"%s\": %d signature removed.\n":
-                        "User ID \"%s\": %d signatures removed.\n",
-                        user,deleted);
-             modified=1;
-           }
-         else
-           tty_printf(_("User ID \"%s\": already clean.\n"),user);
 
-         xfree(user);
-       }
-    }
-
-  return modified;
-}
-
-static int
-menu_clean_uids_from_key(KBNODE keyblock)
-{
-  int modified=clean_uids_from_key(keyblock,0);
-
-  if(modified)
-    {
-      KBNODE node,uidnode=NULL;
-
-      for(node=keyblock->next;node;node=node->next)
-       {
-         if(node->pkt->pkttype==PKT_USER_ID)
-           uidnode=node;
-         else if(uidnode && node->pkt->pkttype==PKT_SIGNATURE
-                 && is_deleted_kbnode(node))
+         clean_one_uid(keyblock,uidnode,opt.verbose,self_only,&uids,&sigs);
+         if(uids)
            {
              const char *reason;
-             char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
-                                       uidnode->pkt->pkt.user_id->len,0);
 
              if(uidnode->pkt->pkt.user_id->is_revoked)
                reason=_("revoked");
@@ -3217,14 +3235,23 @@ menu_clean_uids_from_key(KBNODE keyblock)
 
              tty_printf("User ID \"%s\" compacted: %s\n",user,reason);
 
-             uidnode=NULL;
+             modified=1;
+           }
+         else if(sigs)
+           {
+             tty_printf(sigs==1?
+                        "User ID \"%s\": %d signature removed\n":
+                        "User ID \"%s\": %d signatures removed\n",
+                        user,sigs);
 
-             xfree(user);
+             modified=1;
            }
+         else
+           tty_printf(_("User ID \"%s\": already clean\n"),user);
+
+         xfree(user);
        }
     }
-  else
-    tty_printf("No user IDs are compactable.\n");
 
   return modified;
 }
@@ -3350,9 +3377,11 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
          goto fail;
        }
 
-      /* Note that I'm requesting SIG here and not CERT.  We're making
-        a certification, but it is okay to be a subkey. */
-      revoker_pk->req_usage=PUBKEY_USAGE_SIG;
+      /* Note that I'm requesting CERT here, which usually implies
+        primary keys only, but some casual testing shows that PGP and
+        GnuPG both can handle a designated revokation from a
+        subkey. */
+      revoker_pk->req_usage=PUBKEY_USAGE_CERT;
       rc=get_pubkey_byname(revoker_pk,answer,NULL,NULL,1);
       if(rc)
        {
@@ -3604,6 +3633,167 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
 }
 
 static int
+menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock)
+{
+  int rc,modified=0;
+  PKT_public_key *main_pk;
+  PKT_secret_key *main_sk,*sub_sk=NULL;
+  KBNODE node;
+
+  assert(pub_keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+  assert(sec_keyblock->pkt->pkttype==PKT_SECRET_KEY);
+
+  merge_keys_and_selfsig(pub_keyblock);
+  main_pk=pub_keyblock->pkt->pkt.public_key;
+  main_sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key);
+  keyid_from_pk(main_pk,NULL);
+
+  for(node=pub_keyblock;node;node=node->next)
+    {
+      PKT_public_key *sub_pk=NULL;
+      KBNODE node2,sig_pk=NULL,sig_sk=NULL;
+      char *passphrase;
+
+      if(sub_sk)
+       {
+         free_secret_key(sub_sk);
+         sub_sk=NULL;
+       }
+
+      /* Find a signing subkey with no backsig */
+      if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+       {
+         if(node->pkt->pkt.public_key->pubkey_usage&PUBKEY_USAGE_SIG)
+           {
+             if(node->pkt->pkt.public_key->backsig)
+               tty_printf(_("signing subkey %s is already cross-certified\n"),
+                          keystr_from_pk(node->pkt->pkt.public_key));
+             else
+               sub_pk=node->pkt->pkt.public_key;
+           }
+         else
+           tty_printf(_("subkey %s does not sign and so does"
+                        " not need to be cross-certified\n"),
+                      keystr_from_pk(node->pkt->pkt.public_key));
+       }
+
+      if(!sub_pk)
+       continue;
+
+      /* Find the selected selfsig on this subkey */
+      for(node2=node->next;
+         node2 && node2->pkt->pkttype==PKT_SIGNATURE;
+         node2=node2->next)
+       if(node2->pkt->pkt.signature->version>=4
+          && node2->pkt->pkt.signature->flags.chosen_selfsig)
+         {
+           sig_pk=node2;
+           break;
+         }
+
+      if(!sig_pk)
+       continue;
+
+      /* Find the secret subkey that matches the public subkey */
+      for(node2=sec_keyblock;node2;node2=node2->next)
+       if(node2->pkt->pkttype==PKT_SECRET_SUBKEY
+          && !cmp_public_secret_key(sub_pk,node2->pkt->pkt.secret_key))
+         {
+           sub_sk=copy_secret_key(NULL,node2->pkt->pkt.secret_key);
+           break;
+         }
+
+      if(!sub_sk)
+       {
+         tty_printf(_("no secret subkey for public subkey %s - ignoring\n"),
+                    keystr_from_pk(sub_pk));
+         continue;
+       }
+
+      /* Now finally find the matching selfsig on the secret subkey.
+        We can't use chosen_selfsig here (it's not set for secret
+        keys), so we just pick the selfsig with the right class.
+        This is what menu_expire does as well. */
+      for(node2=node2->next;
+         node2 && node2->pkt->pkttype!=PKT_SECRET_SUBKEY;
+         node2=node2->next)
+       if(node2->pkt->pkttype==PKT_SIGNATURE
+          && node2->pkt->pkt.signature->version>=4
+          && node2->pkt->pkt.signature->keyid[0]==sig_pk->pkt->pkt.signature->keyid[0]
+          && node2->pkt->pkt.signature->keyid[1]==sig_pk->pkt->pkt.signature->keyid[1]
+          && node2->pkt->pkt.signature->sig_class==sig_pk->pkt->pkt.signature->sig_class)
+         {
+           sig_sk=node2;
+           break;
+         }
+
+      /* Now we can get to work.  We have a main key and secret part,
+        a signing subkey with signature and secret part possibly with
+        signature. */
+
+      passphrase=get_last_passphrase();
+      set_next_passphrase(passphrase);
+      xfree(passphrase);
+
+      rc=make_backsig(sig_pk->pkt->pkt.signature,main_pk,sub_pk,sub_sk);
+      if(rc==0)
+       {
+         PKT_signature *newsig;
+         PACKET *newpkt;
+
+         passphrase=get_last_passphrase();
+         set_next_passphrase(passphrase);
+         xfree(passphrase);
+
+         rc=update_keysig_packet(&newsig,sig_pk->pkt->pkt.signature,main_pk,
+                                 NULL,sub_pk,main_sk,NULL,NULL);
+         if(rc==0)
+           {
+             /* Put the new sig into place on the pubkey */
+             newpkt=xmalloc_clear(sizeof(*newpkt));
+             newpkt->pkttype=PKT_SIGNATURE;
+             newpkt->pkt.signature=newsig;
+             free_packet(sig_pk->pkt);
+             xfree(sig_pk->pkt);
+             sig_pk->pkt=newpkt;
+
+             if(sig_sk)
+               {
+                 /* Put the new sig into place on the seckey */
+                 newpkt=xmalloc_clear(sizeof(*newpkt));
+                 newpkt->pkttype=PKT_SIGNATURE;
+                 newpkt->pkt.signature=copy_signature(NULL,newsig);
+                 free_packet(sig_sk->pkt);
+                 xfree(sig_sk->pkt);
+                 sig_sk->pkt=newpkt;
+               }
+
+             modified=1;
+           }
+         else
+           {
+             log_error("update_keysig_packet failed: %s\n",g10_errstr(rc));
+             break;
+           }
+       }
+      else
+       {
+         log_error("make_backsig failed: %s\n",g10_errstr(rc));
+         break;
+       }
+    }
+
+  set_next_passphrase(NULL);
+
+  free_secret_key(main_sk);
+  if(sub_sk)
+    free_secret_key(sub_sk);
+
+  return modified;
+}
+
+
+static int
 change_primary_uid_cb ( PKT_signature *sig, void *opaque )
 {
     byte buf[1];
@@ -3977,6 +4167,219 @@ menu_set_keyserver_url (const char *url,
   return modified;
 }
 
+static int
+menu_set_notation(const char *string,KBNODE pub_keyblock,KBNODE sec_keyblock)
+{
+  PKT_secret_key *sk;    /* copy of the main sk */
+  PKT_public_key *main_pk;
+  PKT_user_id *uid;
+  KBNODE node;
+  u32 keyid[2];
+  int selected, select_all;
+  int modified = 0;
+  char *answer;
+  struct notation *notation;
+
+  no_primary_warning(pub_keyblock);
+
+  if(string)
+    answer=xstrdup(string);
+  else
+    {
+      answer=cpr_get_utf8("keyedit.add_notation",
+                         _("Enter the notation: "));
+      if(answer[0]=='\0' || answer[0]=='\004')
+       {
+         xfree(answer);
+         return 0;
+       }
+    }
+
+  if(ascii_strcasecmp(answer,"none")==0
+     || ascii_strcasecmp(answer,"-")==0)
+    notation=NULL; /* delete them all */
+  else
+    {
+      notation=string_to_notation(answer,0);
+      if(!notation)
+       {
+         xfree(answer);
+         return 0;
+       }
+    }
+
+  xfree(answer);
+
+  select_all = !count_selected_uids (pub_keyblock);
+
+  node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+  sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+  /* Now we can actually change the self signature(s) */
+  main_pk = NULL;
+  uid = NULL;
+  selected = 0;
+  for ( node=pub_keyblock; node; node = node->next )
+    {
+      if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+       break; /* ready */
+
+      if ( node->pkt->pkttype == PKT_PUBLIC_KEY )
+       {
+         main_pk = node->pkt->pkt.public_key;
+         keyid_from_pk( main_pk, keyid );
+       }
+      else if ( node->pkt->pkttype == PKT_USER_ID )
+       {
+         uid = node->pkt->pkt.user_id;
+         selected = select_all || (node->flag & NODFLG_SELUID);
+       }
+      else if ( main_pk && uid && selected
+               && node->pkt->pkttype == PKT_SIGNATURE )
+       {
+         PKT_signature *sig = node->pkt->pkt.signature;
+         if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+              && (uid && (sig->sig_class&~3) == 0x10)
+              && sig->flags.chosen_selfsig)
+           {
+             char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+             if( sig->version < 4 )
+               log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
+                        user);
+             else
+               {
+                 PKT_signature *newsig;
+                 PACKET *newpkt;
+                 int rc,skip=0,addonly=1;
+
+                 if(sig->flags.notation)
+                   {
+                     tty_printf("Current notations for user ID \"%s\":\n",
+                                user);
+                     tty_print_notations(-9,sig);
+                   }
+                 else
+                   {
+                     tty_printf("No notations on user ID \"%s\"\n",user);
+                     if(notation==NULL)
+                       {
+                         /* There are no current notations, so there
+                            is no point in trying to un-set them. */
+                         continue;
+                       }
+                   }
+
+                 if(notation)
+                   {
+                     struct notation *n;
+                     int deleting=0;
+
+                     notation->next=sig_to_notation(sig);
+
+                     for(n=notation->next;n;n=n->next)
+                       if(strcmp(n->name,notation->name)==0)
+                         {
+                           if(notation->value)
+                             {
+                               if(strcmp(n->value,notation->value)==0)
+                                 {
+                                   if(notation->flags.ignore)
+                                     {
+                                       /* Value match with a delete
+                                          flag. */
+                                       n->flags.ignore=1;
+                                       deleting=1;
+                                     }
+                                   else
+                                     {
+                                       /* Adding the same notation
+                                          twice, so don't add it at
+                                          all. */
+                                       skip=1;
+                                       tty_printf("Skipping notation:"
+                                                  " %s=%s\n",
+                                                  notation->name,
+                                                  notation->value);
+                                       break;
+                                     }
+                                 }
+                             }
+                           else
+                             {
+                               /* No value, so it means delete. */
+                               n->flags.ignore=1;
+                               deleting=1;
+                             }
+
+                           if(n->flags.ignore)
+                             {
+                               tty_printf("Removing notation: %s=%s\n",
+                                          n->name,n->value);
+                               addonly=0;
+                             }
+                         }
+
+                     if(!notation->flags.ignore && !skip)
+                       tty_printf("Adding notation: %s=%s\n",
+                                  notation->name,notation->value);
+
+                     /* We tried to delete, but had no matches */
+                     if(notation->flags.ignore && !deleting)
+                       continue;
+                   }
+                 else
+                   {
+                     tty_printf("Removing all notations\n");
+                     addonly=0;
+                   }
+
+                 if(skip
+                    || (!addonly
+                        && !cpr_get_answer_is_yes("keyedit.confirm_notation",
+                                                  _("Proceed? (y/N) "))))
+                   continue;
+
+                 rc = update_keysig_packet (&newsig, sig,
+                                            main_pk, uid, NULL,
+                                            sk,
+                                            keygen_add_notations, notation );
+                 if( rc )
+                   {
+                     log_error ("update_keysig_packet failed: %s\n",
+                                g10_errstr(rc));
+                     free_secret_key( sk );
+                     free_notation(notation);
+                     xfree(user);
+                     return 0;
+                   }
+
+                 /* replace the packet */
+                 newpkt = xmalloc_clear( sizeof *newpkt );
+                 newpkt->pkttype = PKT_SIGNATURE;
+                 newpkt->pkt.signature = newsig;
+                 free_packet( node->pkt );
+                 xfree( node->pkt );
+                 node->pkt = newpkt;
+                 modified = 1;
+
+                 if(notation)
+                   {
+                     /* Snip off the notation list from the sig */
+                     free_notation(notation->next);
+                     notation->next=NULL;
+                   }
+
+                 xfree(user);
+               }
+           }
+       }
+    }
+
+  free_notation(notation);
+  free_secret_key( sk );
+  return modified;
+}
+
 
 /****************
  * Select one user id or remove all selection if index is 0.