* photoid.h, photoid.c (generate_photo_id): Allow passing in a
[gnupg.git] / g10 / keyedit.c
index 46759ef..6e8d944 100644 (file)
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <errno.h>
 #include <assert.h>
 #include <ctype.h>
-
+#ifdef HAVE_LIBREADLINE
+#include <stdio.h>
+#include <readline/readline.h>
+#endif
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
@@ -47,9 +51,12 @@ static void show_prefs( PKT_user_id *uid, PKT_signature *selfsig, int verbose);
 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_delsig( KBNODE pub_keyblock );
+static int menu_clean_sigs_from_uids(KBNODE keyblock);
+static int menu_clean_uids_from_key(KBNODE keyblock);
 static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_addrevoker( KBNODE pub_keyblock,
                            KBNODE sec_keyblock, int sensitive );
@@ -59,6 +66,7 @@ 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_select_uid( KBNODE keyblock, int idx );
+static int menu_select_uid_namehash( KBNODE keyblock, const char *namehash );
 static int menu_select_key( KBNODE keyblock, int idx );
 static int count_uids( KBNODE keyblock );
 static int count_uids_with_flag( KBNODE keyblock, unsigned flag );
@@ -260,7 +268,7 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node,
            char *p = get_user_id( sig->keyid, &n );
            tty_print_utf8_string2(p, n, opt.screen_columns-keystrlen()-26-
                               ((opt.list_options&LIST_SHOW_SIG_EXPIRE)?11:0));
-           m_free(p);
+           xfree(p);
          }
        tty_printf("\n");
 
@@ -418,7 +426,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
        *trust_value=60;
       else if(p[0]=='2' && !p[1])
        *trust_value=120;
-      m_free(p);
+      xfree(p);
     }
 
   tty_printf("\n");
@@ -435,7 +443,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
       trim_spaces(p);
       cpr_kill_prompt();
       *trust_depth=atoi(p);
-      m_free(p);
+      xfree(p);
     }
 
   tty_printf("\n");
@@ -454,7 +462,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
       char *q=p;
       int regexplen=100,ind;
 
-      *regexp=m_alloc(regexplen);
+      *regexp=xmalloc(regexplen);
 
       /* Now mangle the domain the user entered into a regexp.  To do
         this, \-escape everything that isn't alphanumeric, and attach
@@ -474,7 +482,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
          if((regexplen-ind)<3)
            {
              regexplen+=100;
-             *regexp=m_realloc(*regexp,regexplen);
+             *regexp=xrealloc(*regexp,regexplen);
            }
 
          q++;
@@ -484,7 +492,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
       strcat(*regexp,">$");
     }
 
-  m_free(p);
+  xfree(p);
   tty_printf("\n");
 }
 
@@ -495,7 +503,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
  */
 static int
 sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
-          int local, int nonrevocable, int trust )
+          int local, int nonrevocable, int trust, int interactive )
 {
     int rc = 0;
     SK_LIST sk_list = NULL;
@@ -503,7 +511,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
     PKT_secret_key *sk = NULL;
     KBNODE node, uidnode;
     PKT_public_key *primary_pk=NULL;
-    int select_all = !count_selected_uids(keyblock);
+    int select_all = !count_selected_uids(keyblock) || interactive;
     int all_v3=1;
 
     /* Are there any non-v3 sigs on this key already? */
@@ -522,8 +530,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
      * be one which is capable of signing keys.  I can't see a reason
      * why to sign keys using a subkey.  Implementation of USAGE_CERT
      * is just a hack in getkey.c and does not mean that a subkey
-     * marked as certification capable will be used */
-    rc=build_sk_list( locusr, &sk_list, 0, PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT);
+     * marked as certification capable will be used. */
+    rc=build_sk_list( locusr, &sk_list, 0, PUBKEY_USAGE_CERT);
     if( rc )
        goto leave;
 
@@ -572,10 +580,12 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                    force_v4=0;
                  }
            }
-           else if( node->pkt->pkttype == PKT_USER_ID ) {
+           else if( node->pkt->pkttype == PKT_USER_ID )
+             {
                uidnode = (node->flag & NODFLG_MARK_A)? node : NULL;
                if(uidnode)
                  {
+                   int yesreally=0;
                    char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
                                              uidnode->pkt->pkt.user_id->len,
                                              0);
@@ -598,6 +608,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -624,6 +636,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -649,6 +663,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -658,9 +674,20 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                          }
                      }
 
-                   m_free(user);
+                   if(uidnode && interactive && !yesreally)
+                     {
+                       tty_printf(_("User ID \"%s\" is signable.  "),user);
+                       if(!cpr_get_answer_is_yes("sign_uid.sign_okay",
+                                                 _("Sign it? (y/N) ")))
+                         {
+                           uidnode->flag &= ~NODFLG_MARK_A;
+                           uidnode=NULL;
+                         }
+                     }
+
+                   xfree(user);
                  }
-           }
+             }
            else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE
                && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
                if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0]
@@ -688,7 +715,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                            {
                              force_v4=1;
                              node->flag|=NODFLG_DELSIG;
-                             m_free(user);
+                             xfree(user);
                              continue;
                            }
                      }
@@ -712,7 +739,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                in place. */
 
                            node->flag|=NODFLG_DELSIG;
-                           m_free(user);
+                           xfree(user);
                            continue;
                          }
                      }
@@ -737,7 +764,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                in place. */
 
                            node->flag|=NODFLG_DELSIG;
-                           m_free(user);
+                           xfree(user);
                            continue;
                          }
                      }
@@ -759,7 +786,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                      {
                        /* Don't delete the old sig here since this is
                           an --expert thing. */
-                       m_free(user);
+                       xfree(user);
                        continue;
                      }
 
@@ -768,7 +795,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                     write_status_text (STATUS_ALREADY_SIGNED, buf);
                    uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */
 
-                   m_free(user);
+                   xfree(user);
                }
            }
        }
@@ -809,35 +836,42 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
              }
            else
              {
-               char *answer;
-
                tty_printf(_("This key is due to expire on %s.\n"),
                           expirestr_from_pk(primary_pk));
 
-               answer=cpr_get("sign_uid.expire",
-                              _("Do you want your signature to "
-                                "expire at the same time? (Y/n) "));
-               if(answer_is_yes_no_default(answer,1))
+               if(opt.ask_cert_expire)
                  {
-                   /* This fixes the signature timestamp we're going
-                      to make as now.  This is so the expiration date
-                      is exactly correct, and not a few seconds off
-                      (due to the time it takes to answer the
-                      questions, enter the passphrase, etc). */
-                   timestamp=now;
-                   duration=primary_pk->expiredate-now;
-                   force_v4=1;
-                 }
+                   char *answer=cpr_get("sign_uid.expire",
+                                        _("Do you want your signature to "
+                                          "expire at the same time? (Y/n) "));
+                   if(answer_is_yes_no_default(answer,1))
+                     {
+                       /* This fixes the signature timestamp we're
+                          going to make as now.  This is so the
+                          expiration date is exactly correct, and not
+                          a few seconds off (due to the time it takes
+                          to answer the questions, enter the
+                          passphrase, etc). */
+                       timestamp=now;
+                       duration=primary_pk->expiredate-now;
+                       force_v4=1;
+                     }
 
-               cpr_kill_prompt();
-               m_free(answer);
+                   cpr_kill_prompt();
+                   xfree(answer);
+                 }
              }
          }
 
        /* Only ask for duration if we haven't already set it to match
            the expiration of the pk */
-       if(opt.ask_cert_expire && !duration && !selfsig)
-         duration=ask_expire_interval(1);
+       if(!duration && !selfsig)
+         {
+           if(opt.ask_cert_expire)
+             duration=ask_expire_interval(1,opt.def_cert_expire);
+           else
+             duration=parse_expire_string(opt.def_cert_expire);
+         }
 
        if(duration)
          force_v4=1;
@@ -907,7 +941,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                    else
                      tty_printf(_("Invalid selection.\n"));
 
-                   m_free(answer);
+                   xfree(answer);
                  }
              }
 
@@ -918,7 +952,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
        p=get_user_id_native(sk_keyid);
        tty_printf(_("Are you sure that you want to sign this key with your\n"
                     "key \"%s\" (%s)\n"),p,keystr_from_sk(sk));
-       m_free(p);
+       xfree(p);
 
        if(selfsig)
          {
@@ -1033,7 +1067,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                *ret_modified = 1; /* we changed the keyblock */
                update_trust = 1;
 
-               pkt = m_alloc_clear( sizeof *pkt );
+               pkt = xmalloc_clear( sizeof *pkt );
                pkt->pkttype = PKT_SIGNATURE;
                pkt->pkt.signature = sig;
                insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE );
@@ -1141,7 +1175,7 @@ change_passphrase( KBNODE keyblock )
        tty_printf(_("Can't edit this key: %s\n"), g10_errstr(rc));
     else {
        DEK *dek = NULL;
-       STRING2KEY *s2k = m_alloc_secure( sizeof *s2k );
+       STRING2KEY *s2k = xmalloc_secure( sizeof *s2k );
         const char *errtext = NULL;
 
        tty_printf(_("Enter the new passphrase for this secret key.\n\n") );
@@ -1194,12 +1228,12 @@ change_passphrase( KBNODE keyblock )
                break;
            }
        }
-       m_free(s2k);
-       m_free(dek);
+       xfree(s2k);
+       xfree(dek);
     }
 
   leave:
-    m_free( passphrase );
+    xfree( passphrase );
     set_next_passphrase( NULL );
     return changed && !rc;
 }
@@ -1277,6 +1311,7 @@ parse_sign_type(const char *str,int *localsig,int *nonrevokesig,int *trustsig)
   return 1;
 }
 
+\f
 /****************
  * Menu driven key editor.  If seckey_check is true, then a secret key
  * that matches username will be looked for.  If it is false, not all
@@ -1303,7 +1338,7 @@ enum cmdids
     cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
     cmdEXPIRE, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF,
     cmdPREFKS, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST,
-    cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdNOP
+    cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN, cmdNOP
   };
 
 static struct
@@ -1402,9 +1437,58 @@ static struct
     { "enable"  , cmdENABLEKEY , KEYEDIT_NOT_SK, N_("enable key") },
     { "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") },
     { NULL, cmdNONE, 0, NULL }
   };
 
+
+#ifdef HAVE_LIBREADLINE
+
+/* These two functions are used by readline for command completion. */
+
+static char *
+command_generator(const char *text,int state)
+{
+  static int list_index,len;
+  const char *name;
+
+  /* If this is a new word to complete, initialize now.  This includes
+     saving the length of TEXT for efficiency, and initializing the
+     index variable to 0. */
+  if(!state)
+    {
+      list_index=0;
+      len=strlen(text);
+    }
+
+  /* Return the next partial match */
+  while((name=cmds[list_index].name))
+    {
+      /* Only complete commands that have help text */
+      if(cmds[list_index++].desc && strncmp(name,text,len)==0)
+       return strdup(name);
+    }
+
+  return NULL;
+}
+
+static char **
+keyedit_completion(const char *text, int start, int end)
+{
+  /* If we are at the start of a line, we try and command-complete.
+     If not, just do nothing for now. */
+
+  if(start==0)
+    return rl_completion_matches(text,command_generator);
+
+  rl_attempted_completion_over=1;
+
+  return NULL;
+}
+#endif /* HAVE_LIBREADLINE */
+
+
 void
 keyedit_menu( const char *username, STRLIST locusr,
              STRLIST commands, int quiet, int seckey_check )
@@ -1431,7 +1515,20 @@ keyedit_menu( const char *username, STRLIST locusr,
        goto leave;
       }
 
-    /* get the public key */
+#ifdef HAVE_W32_SYSTEM
+    /* Due to Windows peculiarities we need to make sure that the
+       trustdb stale check is done before we open another file
+       (i.e. by searching for a key).  In theory we could make sure
+       that the files are closed after use but the open/close caches
+       inhibits that and flushing the cache right before the stale
+       check is not easy to implement.  Thus we take the easy way out
+       and run the stale check as early as possible.  Note, that for
+       non- W32 platforms it is run indirectly trough a call to
+       get_validity ().  */
+    check_trustdb_stale ();
+#endif
+
+    /* Get the public key */
     rc = get_pubkey_byname (NULL, username, &keyblock, &kdbhd, 1);
     if( rc )
        goto leave;
@@ -1490,6 +1587,7 @@ keyedit_menu( const char *username, STRLIST locusr,
        PKT_public_key *pk=keyblock->pkt->pkt.public_key;
 
        tty_printf("\n");
+
        if( redisplay && !quiet )
          {
            show_key_with_all_names( cur_keyblock, 0, 1, 0, 1, 0 );
@@ -1497,22 +1595,25 @@ keyedit_menu( const char *username, STRLIST locusr,
            redisplay = 0;
          }
        do {
-           m_free(answer);
+           xfree(answer);
            if( have_commands ) {
                if( commands ) {
-                   answer = m_strdup( commands->d );
+                   answer = xstrdup( commands->d );
                    commands = commands->next;
                }
                else if( opt.batch ) {
-                   answer = m_strdup("quit");
+                   answer = xstrdup("quit");
                }
                else
                    have_commands = 0;
            }
-           if( !have_commands ) {
+           if( !have_commands )
+             {
+               tty_enable_completion(keyedit_completion);
                answer = cpr_get_no_help("keyedit.prompt", _("Command> "));
                cpr_kill_prompt();
-           }
+               tty_disable_completion();
+             }
            trim_spaces(answer);
        } while( *answer == '#' );
 
@@ -1598,8 +1699,10 @@ keyedit_menu( const char *username, STRLIST locusr,
            break;
 
          case cmdSELUID:
-           if( menu_select_uid( cur_keyblock, arg_number ) )
-               redisplay = 1;
+           if(strlen(arg_string)==NAMEHASH_LEN*2)
+             redisplay=menu_select_uid_namehash(cur_keyblock,arg_string);
+           else
+             redisplay=menu_select_uid(cur_keyblock,arg_number);
            break;
 
          case cmdSELKEY:
@@ -1616,7 +1719,7 @@ keyedit_menu( const char *username, STRLIST locusr,
 
          case cmdSIGN: /* sign (only the public key) */
            {
-             int localsig=0,nonrevokesig=0,trustsig=0;
+             int localsig=0,nonrevokesig=0,trustsig=0,interactive=0;
 
              if( pk->is_revoked )
                {
@@ -1637,18 +1740,21 @@ keyedit_menu( const char *username, STRLIST locusr,
                    }
                }
 
-             if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock)
-                 && !have_commands )
-               {
-                 if( !cpr_get_answer_is_yes("keyedit.sign_all.okay",
-                                            _("Really sign all user IDs?"
-                                              " (y/N) ")))
-                   {
+             if(count_uids(keyblock) > 1 && !count_selected_uids(keyblock)
+                && !cpr_get_answer_is_yes("keyedit.sign_all.okay",
+                                          _("Really sign all user IDs?"
+                                            " (y/N) ")))
+                {
+                  if(opt.interactive)
+                   interactive=1;
+                 else
+                    {
                      tty_printf(_("Hint: Select the user IDs to sign\n"));
-                     break;
-                   }
-               }
+                      have_commands = 0;
+                      break;
+                    }
 
+                }
              /* What sort of signing are we doing? */
              if(!parse_sign_type(answer,&localsig,&nonrevokesig,&trustsig))
                {
@@ -1657,7 +1763,7 @@ keyedit_menu( const char *username, STRLIST locusr,
                }
 
              sign_uids(keyblock, locusr, &modified,
-                       localsig, nonrevokesig, trustsig);
+                       localsig, nonrevokesig, trustsig, interactive);
            }
            break;
 
@@ -1683,7 +1789,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;
@@ -1868,7 +1974,7 @@ keyedit_menu( const char *username, STRLIST locusr,
            {
              int sensitive=0;
 
-             if(arg_string && ascii_strcasecmp(arg_string,"sensitive")==0)
+             if(ascii_strcasecmp(arg_string,"sensitive")==0)
                sensitive=1;
              if( menu_addrevoker( keyblock, sec_keyblock, sensitive ) ) {
                redisplay = 1;
@@ -2039,9 +2145,32 @@ keyedit_menu( const char *username, STRLIST locusr,
            }
            break;
 
-         case cmdSHOWPHOTO:
-           menu_showphoto(keyblock);
-           break;
+         case cmdSHOWPHOTO:
+           menu_showphoto(keyblock);
+           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;
+               }
+           }
+           break;
 
          case cmdQUIT:
            if( have_commands )
@@ -2097,10 +2226,11 @@ keyedit_menu( const char *username, STRLIST locusr,
     release_kbnode( keyblock );
     release_kbnode( sec_keyblock );
     keydb_release (kdbhd);
-    m_free(answer);
+    xfree(answer);
 }
 
 
+\f
 /****************
  * show preferences of a public keyblock.
  */
@@ -2248,7 +2378,6 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
     }
 }
 
-
 /* This is the version of show_key_with_all_names used when
    opt.with_colons is used.  It prints all available data in a easy to
    parse format and does not translate utf8 */
@@ -2430,7 +2559,8 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
     /* the keys */
     for( node = keyblock; node; node = node->next ) {
        if( node->pkt->pkttype == PKT_PUBLIC_KEY
-           || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) {
+           || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+               && !is_deleted_kbnode(node)) ) {
            PKT_public_key *pk = node->pkt->pkt.public_key;
            const char *otrust="err",*trust="err";
 
@@ -2459,7 +2589,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
                const char *algo=pubkey_algo_to_string(pk->revoked.algo);
                tty_printf(_("This key was revoked on %s by %s key %s\n"),
                           revokestr_from_pk(pk),algo?algo:"?",user);
-               m_free(user);
+               xfree(user);
              }
 
            if(with_revoker)
@@ -2488,7 +2618,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
                        }
 
                      tty_printf ("\n");
-                     m_free(user);
+                     xfree(user);
                    }
              }
 
@@ -2589,7 +2719,8 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
     i = 0;
     for( node = keyblock; node; node = node->next )
       {
-       if( node->pkt->pkttype == PKT_USER_ID )
+       if( node->pkt->pkttype == PKT_USER_ID
+           && !is_deleted_kbnode(node))
          {
            PKT_user_id *uid = node->pkt->pkt.user_id;
            ++i;
@@ -2780,7 +2911,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;
@@ -2846,7 +2978,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 )
@@ -2862,7 +2994,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
     }
 
     /* insert/append to secret keyblock */
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_USER_ID;
     pkt->pkt.user_id = scopy_user_id(uid);
     node = new_kbnode(pkt);
@@ -2870,7 +3002,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
        insert_kbnode( sec_where, node, 0 );
     else
        add_kbnode( sec_keyblock, node );
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_SIGNATURE;
     pkt->pkt.signature = copy_signature(NULL, sig);
     if( sec_where )
@@ -2878,7 +3010,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
     else
        add_kbnode( sec_keyblock, new_kbnode(pkt) );
     /* insert/append to public keyblock */
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_USER_ID;
     pkt->pkt.user_id = uid;
     node = new_kbnode(pkt);
@@ -2886,7 +3018,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
        insert_kbnode( pub_where, node, 0 );
     else
        add_kbnode( pub_keyblock, node );
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_SIGNATURE;
     pkt->pkt.signature = copy_signature(NULL, sig);
     if( pub_where )
@@ -3023,6 +3155,81 @@ menu_delsig( KBNODE pub_keyblock )
     return changed;
 }
 
+static int
+menu_clean_sigs_from_uids(KBNODE keyblock)
+{
+  KBNODE uidnode;
+  int modified=0;
+  int select_all=!count_selected_uids(keyblock);
+
+  for(uidnode=keyblock->next;uidnode;uidnode=uidnode->next)
+    {
+      if(uidnode->pkt->pkttype==PKT_USER_ID
+        && (uidnode->flag&NODFLG_SELUID || select_all))
+       {
+         int deleted;
+         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))
+           {
+             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");
+             else if(uidnode->pkt->pkt.user_id->is_expired)
+               reason=_("expired");
+             else
+               reason=_("invalid");
+
+             tty_printf("User ID \"%s\" compacted: %s\n",user,reason);
+
+             uidnode=NULL;
+
+             xfree(user);
+           }
+       }
+    }
+  else
+    tty_printf("No user IDs are compactable.\n");
+
+  return modified;
+}
 
 /****************
  * Remove some of the secondary keys
@@ -3133,7 +3340,7 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
       if(revoker_pk)
        free_public_key(revoker_pk);
 
-      revoker_pk=m_alloc_clear(sizeof(*revoker_pk));
+      revoker_pk=xmalloc_clear(sizeof(*revoker_pk));
 
       tty_printf("\n");
 
@@ -3141,7 +3348,7 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
                          _("Enter the user ID of the designated revoker: "));
       if(answer[0]=='\0' || answer[0]=='\004')
        {
-         m_free(answer);
+         xfree(answer);
          goto fail;
        }
 
@@ -3152,11 +3359,11 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
       if(rc)
        {
          log_error (_("key \"%s\" not found: %s\n"),answer,g10_errstr(rc));
-         m_free(answer);
+         xfree(answer);
          continue;
        }
 
-      m_free(answer);
+      xfree(answer);
 
       fingerprint_from_pk(revoker_pk,revkey.fpr,&fprlen);
       if(fprlen!=20)
@@ -3246,13 +3453,13 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
   sk=NULL;
 
   /* insert into secret keyblock */
-  pkt = m_alloc_clear( sizeof *pkt );
+  pkt = xmalloc_clear( sizeof *pkt );
   pkt->pkttype = PKT_SIGNATURE;
   pkt->pkt.signature = copy_signature(NULL, sig);
   insert_kbnode( sec_keyblock, new_kbnode(pkt), PKT_SIGNATURE );
 
   /* insert into public keyblock */
-  pkt = m_alloc_clear( sizeof *pkt );
+  pkt = xmalloc_clear( sizeof *pkt );
   pkt->pkttype = PKT_SIGNATURE;
   pkt->pkt.signature = sig;
   insert_kbnode( pub_keyblock, new_kbnode(pkt), PKT_SIGNATURE );
@@ -3374,18 +3581,18 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
                    return 0;
                }
                /* replace the packet */
-               newpkt = m_alloc_clear( sizeof *newpkt );
+               newpkt = xmalloc_clear( sizeof *newpkt );
                newpkt->pkttype = PKT_SIGNATURE;
                newpkt->pkt.signature = newsig;
                free_packet( node->pkt );
-               m_free( node->pkt );
+               xfree( node->pkt );
                node->pkt = newpkt;
                if( sn ) {
-                   newpkt = m_alloc_clear( sizeof *newpkt );
+                   newpkt = xmalloc_clear( sizeof *newpkt );
                    newpkt->pkttype = PKT_SIGNATURE;
                    newpkt->pkt.signature = copy_signature( NULL, newsig );
                    free_packet( sn->pkt );
-                   m_free( sn->pkt );
+                   xfree( sn->pkt );
                    sn->pkt = newpkt;
                }
                sub_pk = NULL;
@@ -3480,7 +3687,7 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock )
 
                log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
                         user);
-               m_free(user);
+               xfree(user);
              }
              else {
                /* This is a selfsignature which is to be replaced.
@@ -3524,11 +3731,11 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock )
                         return 0;
                     }
                     /* replace the packet */
-                    newpkt = m_alloc_clear( sizeof *newpkt );
+                    newpkt = xmalloc_clear( sizeof *newpkt );
                     newpkt->pkttype = PKT_SIGNATURE;
                     newpkt->pkt.signature = newsig;
                     free_packet( node->pkt );
-                    m_free( node->pkt );
+                    xfree( node->pkt );
                     node->pkt = newpkt;
                     modified = 1;
                }
@@ -3590,7 +3797,7 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock )
 
                log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
                         user);
-               m_free(user);
+               xfree(user);
              }
              else {
                /* This is a selfsignature which is to be replaced 
@@ -3612,11 +3819,11 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock )
                     return 0;
                 }
                 /* replace the packet */
-                newpkt = m_alloc_clear( sizeof *newpkt );
+                newpkt = xmalloc_clear( sizeof *newpkt );
                 newpkt->pkttype = PKT_SIGNATURE;
                 newpkt->pkt.signature = newsig;
                 free_packet( node->pkt );
-                m_free( node->pkt );
+                xfree( node->pkt );
                 node->pkt = newpkt;
                 modified = 1;
              }
@@ -3645,14 +3852,14 @@ menu_set_keyserver_url (const char *url,
   no_primary_warning(pub_keyblock);
 
   if(url)
-    answer=m_strdup(url);
+    answer=xstrdup(url);
   else
     {
       answer=cpr_get_utf8("keyedit.add_keyserver",
                          _("Enter your preferred keyserver URL: "));
       if(answer[0]=='\0' || answer[0]=='\004')
        {
-         m_free(answer);
+         xfree(answer);
          return 0;
        }
     }
@@ -3664,13 +3871,13 @@ menu_set_keyserver_url (const char *url,
       struct keyserver_spec *keyserver=NULL;
       /* Sanity check the format */
       keyserver=parse_keyserver_uri(answer,1,NULL,0);
-      m_free(answer);
+      xfree(answer);
       if(!keyserver)
        {
          log_info(_("could not parse keyserver URL\n"));
          return 0;
        }
-      uri=m_strdup(keyserver->uri);
+      uri=xstrdup(keyserver->uri);
       free_keyserver_spec(keyserver);
     }
 
@@ -3749,25 +3956,25 @@ menu_set_keyserver_url (const char *url,
                      log_error ("update_keysig_packet failed: %s\n",
                                 g10_errstr(rc));
                      free_secret_key( sk );
-                     m_free(uri);
+                     xfree(uri);
                      return 0;
                    }
                  /* replace the packet */
-                 newpkt = m_alloc_clear( sizeof *newpkt );
+                 newpkt = xmalloc_clear( sizeof *newpkt );
                  newpkt->pkttype = PKT_SIGNATURE;
                  newpkt->pkt.signature = newsig;
                  free_packet( node->pkt );
-                 m_free( node->pkt );
+                 xfree( node->pkt );
                  node->pkt = newpkt;
                  modified = 1;
                }
 
-             m_free(user);
+             xfree(user);
            }
        }
     }
 
-  m_free(uri);
+  xfree(uri);
   free_secret_key( sk );
   return modified;
 }
@@ -3818,6 +4025,45 @@ menu_select_uid( KBNODE keyblock, int idx )
     return 1;
 }
 
+/* Search in the keyblock for a uid that matches namehash */
+static int
+menu_select_uid_namehash( KBNODE keyblock, const char *namehash )
+{
+  byte hash[NAMEHASH_LEN];
+  KBNODE node;
+  int i;
+
+  assert(strlen(namehash)==NAMEHASH_LEN*2);
+
+  for(i=0;i<NAMEHASH_LEN;i++)
+    hash[i]=hextobyte(&namehash[i*2]);
+
+  for(node=keyblock->next;node;node=node->next)
+    {
+      if(node->pkt->pkttype==PKT_USER_ID)
+       {
+         namehash_from_uid(node->pkt->pkt.user_id);
+         if(memcmp(node->pkt->pkt.user_id->namehash,hash,NAMEHASH_LEN)==0)
+           {
+             if(node->flag&NODFLG_SELUID)
+               node->flag &= ~NODFLG_SELUID;
+             else
+               node->flag |= NODFLG_SELUID;
+
+             break;
+           }
+       }
+    }
+
+    if(!node)
+      {
+       tty_printf(_("No user ID with hash %s\n"),namehash);
+       return 0;
+      }
+
+  return 1;
+}
+
 /****************
  * Select secondary keys
  * Returns: True if the selection changed;
@@ -3944,7 +4190,7 @@ static void
 ask_revoke_sig( KBNODE keyblock, KBNODE node )
 {
     int doit=0;
-    char *p;
+    PKT_user_id *uid;
     PKT_signature *sig = node->pkt->pkt.signature;
     KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID );
 
@@ -3953,15 +4199,33 @@ ask_revoke_sig( KBNODE keyblock, KBNODE node )
        return;
     }
 
-    p=utf8_to_native(unode->pkt->pkt.user_id->name,
-                    unode->pkt->pkt.user_id->len,0);
-    tty_printf(_("user ID: \"%s\"\n"),p);
-    m_free(p);
+    uid=unode->pkt->pkt.user_id;
 
-    tty_printf(_("signed by your key %s on %s%s%s\n"),
-              keystr(sig->keyid),datestr_from_sig(sig),
-              sig->flags.exportable?"":_(" (non-exportable)"),"");
+    if(opt.with_colons)
+      {
+       if(uid->attrib_data)
+         printf("uat:::::::::%u %lu",uid->numattribs,uid->attrib_len);
+       else
+         {
+           printf("uid:::::::::");
+           print_string (stdout, uid->name, uid->len, ':');
+         }
+
+       printf("\n");
 
+       print_and_check_one_sig_colon(keyblock,node,NULL,NULL,NULL,NULL,1);
+      }
+    else
+      {
+       char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+                        unode->pkt->pkt.user_id->len,0);
+       tty_printf(_("user ID: \"%s\"\n"),p);
+       xfree(p);
+
+       tty_printf(_("signed by your key %s on %s%s%s\n"),
+                  keystr(sig->keyid),datestr_from_sig(sig),
+                  sig->flags.exportable?"":_(" (non-exportable)"),"");
+      }
     if(sig->flags.expired)
       {
        tty_printf(_("This signature expired on %s.\n"),
@@ -4102,7 +4366,7 @@ menu_revsig( KBNODE keyblock )
        attrib.non_exportable=!node->pkt->pkt.signature->flags.exportable;
 
        node->flag &= ~NODFLG_MARK_A;
-       sk = m_alloc_secure_clear( sizeof *sk );
+       sk = xmalloc_secure_clear( sizeof *sk );
        if( get_seckey( sk, node->pkt->pkt.signature->keyid ) ) {
            log_info(_("no secret key\n"));
            continue;
@@ -4126,7 +4390,7 @@ menu_revsig( KBNODE keyblock )
        if(primary_pk->keyid[0]==sig->keyid[0] &&
           primary_pk->keyid[1]==sig->keyid[1])
          unode->pkt->pkt.user_id->is_revoked=1;
-       pkt = m_alloc_clear( sizeof *pkt );
+       pkt = xmalloc_clear( sizeof *pkt );
        pkt->pkttype = PKT_SIGNATURE;
        pkt->pkt.signature = sig;
        insert_kbnode( unode, new_kbnode(pkt), 0 );
@@ -4175,7 +4439,7 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock )
          {
            char *user=utf8_to_native(uid->name,uid->len,0);
            log_info(_("user ID \"%s\" is already revoked\n"),user);
-           m_free(user);
+           xfree(user);
          }
        else
          {
@@ -4212,7 +4476,7 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock )
              }
            else
              {
-               pkt = m_alloc_clear( sizeof *pkt );
+               pkt = xmalloc_clear( sizeof *pkt );
                pkt->pkttype = PKT_SIGNATURE;
                pkt->pkt.signature = sig;
                insert_kbnode( node, new_kbnode(pkt), 0 );
@@ -4277,7 +4541,7 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
 
   changed = 1; /* we changed the keyblock */
 
-  pkt = m_alloc_clear( sizeof *pkt );
+  pkt = xmalloc_clear( sizeof *pkt );
   pkt->pkttype = PKT_SIGNATURE;
   pkt->pkt.signature = sig;
   insert_kbnode( pub_keyblock, new_kbnode(pkt), 0 );
@@ -4338,7 +4602,7 @@ menu_revsubkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
            }
            changed = 1; /* we changed the keyblock */
 
-           pkt = m_alloc_clear( sizeof *pkt );
+           pkt = xmalloc_clear( sizeof *pkt );
            pkt->pkttype = PKT_SIGNATURE;
            pkt->pkt.signature = sig;
            insert_kbnode( node, new_kbnode(pkt), 0 );