(parse_dn_part): Map common OIDs to human readable
[gnupg.git] / g10 / import.c
index ccc6651..9c32324 100644 (file)
@@ -1,5 +1,6 @@
-/* import.c
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+/* import.c - Import OpenPGP key material
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ *               2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -34,6 +35,7 @@
 #include "trustdb.h"
 #include "main.h"
 #include "i18n.h"
+#include "ttyio.h"
 #include "status.h"
 #include "keyserver-internal.h"
 
@@ -51,23 +53,24 @@ struct stats_s {
     ulong secret_imported;
     ulong secret_dups;
     ulong skipped_new_keys;
+    ulong not_imported;
 };
 
 
-static int import( IOBUF inp, int fast, const char* fname,
-                   struct stats_s *stats );
-static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
+static int import( iobuf_t inp, const char* fname,
+                   struct stats_s *stats, unsigned int options );
+static int read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root );
 static void revocation_present(KBNODE keyblock);
-static void remove_bad_stuff (KBNODE keyblock);
-static int import_one( const char *fname, KBNODE keyblock, int fast,
-                       struct stats_s *stats);
+static int import_one( const char *fname, KBNODE keyblock,
+                       struct stats_s *stats, unsigned int options);
 static int import_secret_one( const char *fname, KBNODE keyblock,
-                              struct stats_s *stats );
+                              struct stats_s *stats, unsigned int options);
 static int import_revoke_cert( const char *fname, KBNODE node,
                                struct stats_s *stats);
 static int chk_self_sigs( const char *fname, KBNODE keyblock,
-                         PKT_public_key *pk, u32 *keyid );
-static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid );
+                         PKT_public_key *pk, u32 *keyid, int *non_self );
+static int delete_inv_parts( const char *fname, KBNODE keyblock,
+                            u32 *keyid, unsigned int options );
 static int merge_blocks( const char *fname, KBNODE keyblock_orig,
                         KBNODE keyblock, u32 *keyid,
                         int *n_uids, int *n_sigs, int *n_subk );
@@ -80,17 +83,32 @@ static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
 static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
                             const char *fname, u32 *keyid );
 
+int
+parse_import_options(char *str,unsigned int *options)
+{
+  struct parse_options import_opts[]=
+    {
+      {"allow-local-sigs",IMPORT_ALLOW_LOCAL_SIGS},
+      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG},
+      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG},
+      {"fast-import",IMPORT_FAST_IMPORT},
+      {"convert-sk-to-pk",IMPORT_SK2PK},
+      {NULL,0}
+    };
+
+  return parse_options(str,options,import_opts);
+}
 
 void *
 import_new_stats_handle (void)
 {
-    return m_alloc_clear ( sizeof (struct stats_s) );
+    return xcalloc (1, sizeof (struct stats_s) );
 }
 
 void
 import_release_stats_handle (void *p)
 {
-    m_free (p);
+    xfree (p);
 }
 
 /****************
@@ -124,62 +142,79 @@ import_release_stats_handle (void *p)
  *  Key revocation certificates have special handling.
  *
  */
-void
-import_keys( char **fnames, int nnames, int fast, void *stats_handle )
+static int
+import_keys_internal( iobuf_t inp, char **fnames, int nnames,
+                     void *stats_handle, unsigned int options )
 {
-    int i;
+    int i, rc = 0;
     struct stats_s *stats = stats_handle;
 
     if (!stats)
         stats = import_new_stats_handle ();
 
-    if( !fnames && !nnames )
-       nnames = 1;  /* Ohh what a ugly hack to jump into the loop */
-
-    for(i=0; i < nnames; i++ ) {
-       const char *fname = fnames? fnames[i] : NULL;
-       IOBUF inp = iobuf_open(fname);
-       if( !fname )
-           fname = "[stdin]";
-       if( !inp )
-           log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
-       else {
-           int rc = import( inp, fast, fname, stats );
-           iobuf_close(inp);
-           if( rc )
-               log_error("import from `%s' failed: %s\n", fname,
-                                                          g10_errstr(rc) );
+    if (inp) {
+        rc = import( inp, "[stream]", stats, options);
+    }
+    else {
+        if( !fnames && !nnames )
+           nnames = 1;  /* Ohh what a ugly hack to jump into the loop */
+
+       for(i=0; i < nnames; i++ ) {
+           const char *fname = fnames? fnames[i] : NULL;
+           iobuf_t inp2 = iobuf_open(fname);
+           if( !fname )
+               fname = "[stdin]";
+           if( !inp2 )
+               log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+           else {
+               rc = import( inp2, fname, stats, options );
+               iobuf_close(inp2);
+                /* Must invalidate that ugly cache to actually close it. */
+                iobuf_ioctl (NULL, 2, 0, (char*)fname);
+               if( rc )
+                   log_error("import from `%s' failed: %s\n", fname,
+                                     gpg_strerror (rc) );
+           }
+           if( !fname )
+               break;
        }
-       if( !fname )
-           break;
     }
     if (!stats_handle) {
         import_print_stats (stats);
         import_release_stats_handle (stats);
     }
+    /* If no fast import and the trustdb is dirty (i.e. we added a key
+       or userID that had something other than a selfsig, a signature
+       that was other than a selfsig, or any revocation), then
+       update/check the trustdb if the user specified by setting
+       interactive or by not setting no-auto-check-trustdb */
+    if (!(options&IMPORT_FAST_IMPORT) && trustdb_pending_check())
+      {
+       if (opt.interactive)
+         update_trustdb();
+       else if (!opt.no_auto_check_trustdb)
+         check_trustdb();
+      }
 
+    return rc;
 }
 
-int
-import_keys_stream( IOBUF inp, int fast, void *stats_handle )
+void
+import_keys( char **fnames, int nnames,
+            void *stats_handle, unsigned int options )
 {
-    int rc = 0;
-    struct stats_s *stats = stats_handle;
-
-    if (!stats)
-        stats = import_new_stats_handle ();
-
-    rc = import( inp, fast, "[stream]", stats);
-    if (!stats_handle) {
-        import_print_stats (stats);
-        import_release_stats_handle (stats);
-    }
+    import_keys_internal( NULL, fnames, nnames, stats_handle, options);
+}
 
-    return rc;
+int
+import_keys_stream( iobuf_t inp, void *stats_handle, unsigned int options )
+{
+    return import_keys_internal( inp, NULL, 0, stats_handle, options);
 }
 
 static int
-import( IOBUF inp, int fast, const char* fname, struct stats_s *stats )
+import( iobuf_t inp, const char* fname,
+       struct stats_s *stats, unsigned int options )
 {
     PACKET *pending_pkt = NULL;
     KBNODE keyblock;
@@ -188,17 +223,16 @@ import( IOBUF inp, int fast, const char* fname, struct stats_s *stats )
     getkey_disable_caches();
 
     if( !opt.no_armor ) { /* armored reading is not disabled */
-       armor_filter_context_t *afx = m_alloc_clear( sizeof *afx );
+       armor_filter_context_t *afx = xcalloc (1, sizeof *afx );
        afx->only_keyblocks = 1;
        iobuf_push_filter2( inp, armor_filter, afx, 1 );
     }
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
-        remove_bad_stuff (keyblock);
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, fast, stats );
+           rc = import_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
-                rc = import_secret_one( fname, keyblock, stats );
+                rc = import_secret_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SIGNATURE
                 && keyblock->pkt->pkt.signature->sig_class == 0x20 )
            rc = import_revoke_cert( fname, keyblock, stats );
@@ -207,15 +241,17 @@ import( IOBUF inp, int fast, const char* fname, struct stats_s *stats )
                                            keyblock->pkt->pkttype );
        }
        release_kbnode(keyblock);
+        /* fixme: we should increment the not imported counter but this
+           does only make sense if we keep on going despite of errors. */
        if( rc )
            break;
        if( !(++stats->count % 100) && !opt.quiet )
-           log_info(_("%lu keys so far processed\n"), stats->count );
+           log_info(_("%lu keys processed so far\n"), stats->count );
     }
     if( rc == -1 )
        rc = 0;
-    else if( rc && rc != G10ERR_INV_KEYRING )
-       log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc));
+    else if( rc && rc != GPG_ERR_INV_KEYRING )
+       log_error( _("error reading `%s': %s\n"), fname, gpg_strerror (rc));
 
     return rc;
 }
@@ -255,11 +291,13 @@ import_print_stats (void *hd)
            log_info(_("  secret keys imported: %lu\n"), stats->secret_imported );
        if( stats->secret_dups )
            log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups );
+       if( stats->not_imported )
+           log_info(_("          not imported: %lu\n"), stats->not_imported );
     }
 
     if( is_status_enabled() ) {
-       char buf[13*20];
-       sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+       char buf[14*20];
+       sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
                stats->count,
                stats->no_user_id,
                stats->imported,
@@ -272,7 +310,8 @@ import_print_stats (void *hd)
                stats->secret_read,
                stats->secret_imported,
                stats->secret_dups,
-               stats->skipped_new_keys );
+               stats->skipped_new_keys,
+                stats->not_imported );
        write_status_text( STATUS_IMPORT_RES, buf );
     }
 }
@@ -285,7 +324,7 @@ import_print_stats (void *hd)
  * Retunr: 0 = okay, -1 no more blocks or another errorcode.
  */
 static int
-read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
+read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root )
 {
     int rc;
     PACKET *pkt;
@@ -299,13 +338,13 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     }
     else
        in_cert = 0;
-    pkt = m_alloc( sizeof *pkt );
+    pkt = xmalloc ( sizeof *pkt );
     init_packet(pkt);
     while( (rc=parse_packet(a, pkt)) != -1 ) {
        if( rc ) {  /* ignore errors */
-           if( rc != G10ERR_UNKNOWN_PACKET ) {
-               log_error("read_block: read error: %s\n", g10_errstr(rc) );
-               rc = G10ERR_INV_KEYRING;
+           if( rc != GPG_ERR_UNKNOWN_PACKET ) {
+               log_error("read_block: read error: %s\n", gpg_strerror (rc) );
+               rc = GPG_ERR_INV_KEYRING;
                goto ready;
            }
            free_packet( pkt );
@@ -327,11 +366,11 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
          case PKT_COMPRESSED:
            if( pkt->pkt.compressed->algorithm < 1
                || pkt->pkt.compressed->algorithm > 2 ) {
-               rc = G10ERR_COMPR_ALGO;
+               rc = GPG_ERR_COMPR_ALGO;
                goto ready;
            }
            {
-               compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx );
+               compress_filter_context_t *cfx = xcalloc (1, sizeof *cfx );
                cfx->algo = pkt->pkt.compressed->algorithm;
                pkt->pkt.compressed->buf = NULL;
                iobuf_push_filter2( a, compress_filter, cfx, 1 );
@@ -360,7 +399,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
                    root = new_kbnode( pkt );
                else
                    add_kbnode( root, new_kbnode( pkt ) );
-               pkt = m_alloc( sizeof *pkt );
+               pkt = xmalloc ( sizeof *pkt );
            }
            init_packet(pkt);
            break;
@@ -375,115 +414,121 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     else
        *ret_root = root;
     free_packet( pkt );
-    m_free( pkt );
+    xfree ( pkt );
     return rc;
 }
 
-
-static void
-remove_bad_stuff (KBNODE keyblock)
-{
-    KBNODE node;
-
-    for (node=keyblock; node; node = node->next ) {
-        if( node->pkt->pkttype == PKT_SIGNATURE ) {
-            /* delete the subpackets we used to use for the
-               verification cache */
-            delete_sig_subpkt (node->pkt->pkt.signature->unhashed,
-                               SIGSUBPKT_PRIV_VERIFY_CACHE);
-        }
-    }
-}
-
-/* Clean the subkeys on a pk so that they each have at most 1 binding
-   sig and at most 1 revocation sig.  This works based solely on the
-   timestamps like the rest of gpg.  If the standard does get
-   revocation targets, this may need to be revised. */
+/* Walk through the subkeys on a pk to find if we have the PKS
+   disease: multiple subkeys with their binding sigs stripped, and the
+   sig for the first subkey placed after the last subkey.  That is,
+   instead of "pk uid sig sub1 bind1 sub2 bind2 sub3 bind3" we have
+   "pk uid sig sub1 sub2 sub3 bind1".  We can't do anything about sub2
+   and sub3, as they are already lost, but we can try and rescue sub1
+   by reordering the keyblock so that it reads "pk uid sig sub1 bind1
+   sub2 sub3".  Returns TRUE if the keyblock was modified. */
 
 static int
-clean_subkeys(KBNODE keyblock,u32 *keyid)
+fix_pks_corruption(KBNODE keyblock)
 {
-  int removed=0;
-  KBNODE node,sknode=keyblock;
+  int changed=0,keycount=0;
+  KBNODE node,last=NULL,sknode=NULL;
 
-  while((sknode=find_kbnode(sknode,PKT_PUBLIC_SUBKEY)))
+  /* First determine if we have the problem at all.  Look for 2 or
+     more subkeys in a row, followed by a single binding sig. */
+  for(node=keyblock;node;last=node,node=node->next)
     {
-      KBNODE bsnode=NULL,rsnode=NULL;
-      u32 bsdate=0,rsdate=0;
-
-      sknode=sknode->next;
-
-      for(node=sknode;node;node=node->next)
+      if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
        {
-         if(node->pkt->pkttype==PKT_SIGNATURE)
-           {
-             PKT_signature *sig=node->pkt->pkt.signature;
-
-             /* We're only interested in valid sigs */
-             if(check_key_signature(keyblock,node,NULL))
-               continue;
-
-             if(IS_SUBKEY_SIG(sig) && bsdate<=sig->timestamp)
-               {
-                 bsnode=node;
-                 bsdate=sig->timestamp;
-               }
-             else if(IS_SUBKEY_REV(sig) && rsdate<=sig->timestamp)
-               {
-                 rsnode=node;
-                 rsdate=sig->timestamp;
-               }
-             /* If it's not a subkey sig or rev, then it shouldn't be
-                 here so ignore it. */
-           }
-         else
-           break;
+         keycount++;
+         if(!sknode)
+           sknode=node;
        }
+      else if(node->pkt->pkttype==PKT_SIGNATURE &&
+             node->pkt->pkt.signature->sig_class==0x18 &&
+             keycount>=2 && node->next==NULL)
+       {
+         /* We might have the problem, as this key has two subkeys in
+            a row without any intervening packets. */
 
-      /* We now know the most recent binding sig and revocation sig
-         (if any).  If the binding sig is more recent than the
-         revocation sig, strip everything but the binding sig.  If the
-         revocation sig is more recent than the binding sig, strip
-         everything but the binding sig and the revocation sig. */
+         /* Sanity check */
+         if(last==NULL)
+           break;
 
-      if(bsdate>=rsdate)
-       {
-         rsnode=NULL;
-         rsdate=0;
-       }
+         /* Temporarily attach node to sknode. */
+         node->next=sknode->next;
+         sknode->next=node;
+         last->next=NULL;
 
-     for(node=sknode;node;node=node->next)
-       {
-         if(node->pkt->pkttype==PKT_SIGNATURE)
+         /* Note we aren't checking whether this binding sig is a
+            selfsig.  This is not necessary here as the subkey and
+            binding sig will be rejected later if that is the
+            case. */
+         if(check_key_signature(keyblock,node,NULL))
            {
-             PKT_signature *sig=node->pkt->pkt.signature;
-
-             if(IS_SUBKEY_SIG(sig) && node!=bsnode)
-               {
-                 delete_kbnode(node);
-                 removed++;
-               }
-             else if(IS_SUBKEY_REV(sig) && node!=rsnode)
-               {
-                 delete_kbnode(node);
-                 removed++;
-               }
+             /* Not a match, so undo the changes. */
+             sknode->next=node->next;
+             last->next=node;
+             node->next=NULL;
+             break;
            }
          else
-           break;
+           {
+             sknode->flag |= 1; /* Mark it good so we don't need to
+                                    check it again */
+             changed=1;
+             break;
+           }
        }
+      else
+       keycount=0;
     }
 
-  if(removed)
-    {
-      log_info(_("key %08lX: removed multiple subkey binding\n"),
-              (ulong)keyid[1]);
-      commit_kbnode(&keyblock);
-    }
+  return changed;
+}
+
 
-  return removed;
+static void
+print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
+{
+  byte array[MAX_FINGERPRINT_LEN], *s;
+  char buf[MAX_FINGERPRINT_LEN*2+30], *p;
+  size_t i, n;
+
+  sprintf (buf, "%u ", reason);
+  p = buf + strlen (buf);
+
+  if (pk)
+    fingerprint_from_pk (pk, array, &n);
+  else
+    fingerprint_from_sk (sk, array, &n);
+  s = array;
+  for (i=0; i < n ; i++, s++, p += 2)
+    sprintf (p, "%02X", *s);
+
+  write_status_text (STATUS_IMPORT_OK, buf);
 }
 
+void
+print_import_check (PKT_public_key * pk, PKT_user_id * id)
+{
+    char * buf;
+    byte fpr[24];
+    u32 keyid[2];
+    size_t i, pos = 0, n;
+
+    buf = xmalloc (17+41+id->len+32);
+    keyid_from_pk (pk, keyid);
+    sprintf (buf, "%08X%08X ", keyid[0], keyid[1]);
+    pos = 17;
+    fingerprint_from_pk (pk, fpr, &n);
+    for (i = 0; i < n; i++, pos += 2)
+        sprintf (buf+pos, "%02X", fpr[i]);
+    strcat (buf, " ");
+    pos += 1;
+    strcat (buf, id->name);
+    write_status_text (STATUS_IMPORT_CHECK, buf);
+    xfree (buf);
+}
 
 /****************
  * Try to import one keyblock. Return an error only in serious cases, but
@@ -492,8 +537,8 @@ clean_subkeys(KBNODE keyblock,u32 *keyid)
  * which called g10.
  */
 static int
-import_one( const char *fname, KBNODE keyblock, int fast,
-            struct stats_s *stats )
+import_one( const char *fname, KBNODE keyblock,
+            struct stats_s *stats, unsigned int options )
 {
     PKT_public_key *pk;
     PKT_public_key *pk_orig;
@@ -503,6 +548,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     int rc = 0;
     int new_key = 0;
     int mod_key = 0;
+    int non_self = 0;
 
     /* get the key and print some info about it */
     node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
@@ -513,7 +559,11 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if( opt.verbose ) {
+    if(pk->pubkey_algo==PUBKEY_ALGO_ELGAMAL)
+      log_info(_("NOTE: Elgamal primary key detected - "
+                "this may take some time to import\n"));
+
+    if( opt.verbose && !opt.interactive ) {
        log_info( "pub  %4u%c/%08lX %s   ",
                  nbits_from_pk( pk ),
                  pubkey_letter( pk->pubkey_algo ),
@@ -527,9 +577,27 @@ import_one( const char *fname, KBNODE keyblock, int fast,
        log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]);
        return 0;
     }
+    
+    if (opt.interactive) {
+        if(is_status_enabled())
+         print_import_check (pk, uidnode->pkt->pkt.user_id);
+       merge_keys_and_selfsig (keyblock);
+        tty_printf ("\n");
+        show_basic_key_info (keyblock);
+        tty_printf ("\n");
+        if (!cpr_get_answer_is_yes ("import.okay",
+                                    "Do you want to import this key? (y/N) "))
+            return 0;
+    }
 
     clear_kbnode_flags( keyblock );
-    rc = chk_self_sigs( fname, keyblock , pk, keyid );
+
+    if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock)
+       && opt.verbose)
+      log_info(_("key %08lX: PKS subkey corruption repaired\n"),
+              (ulong)keyid[1]);
+
+    rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self );
     if( rc )
        return rc== -1? 0:rc;
 
@@ -543,31 +611,29 @@ import_one( const char *fname, KBNODE keyblock, int fast,
            node->flag |= 1;
            log_info( _("key %08lX: accepted non self-signed user ID '%s'\n"),
                      (ulong)keyid[1],user);
-           m_free(user);
+           xfree (user);
          }
 
-    if( !delete_inv_parts( fname, keyblock, keyid ) ) {
-       if( !opt.quiet ) {
-           log_info( _("key %08lX: no valid user IDs\n"),
-                                                       (ulong)keyid[1]);
+    if( !delete_inv_parts( fname, keyblock, keyid, options ) ) {
+        log_error ( _("key %08lX: no valid user IDs\n"), (ulong)keyid[1]);
+       if( !opt.quiet )
            log_info(_("this may be caused by a missing self-signature\n"));
-       }
        stats->no_user_id++;
        return 0;
     }
 
     /* do we have this key already in one of our pubrings ? */
-    pk_orig = m_alloc_clear( sizeof *pk_orig );
-    rc = get_pubkey( pk_orig, keyid );
-    if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) {
+    pk_orig = xcalloc (1, sizeof *pk_orig );
+    rc = get_pubkey_fast ( pk_orig, keyid );
+    if( rc && gpg_err_code (rc) != GPG_ERR_NO_PUBKEY
+        && gpg_err_code (rc) != GPG_ERR_UNUSABLE_PUBKEY ) {
        log_error( _("key %08lX: public key not found: %s\n"),
-                               (ulong)keyid[1], g10_errstr(rc));
+                               (ulong)keyid[1], gpg_strerror (rc));
     }
     else if ( rc && opt.merge_only ) {
        if( opt.verbose )
            log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] );
        rc = 0;
-       fast = 1; /* so that we don't get into the trustdb update */
        stats->skipped_new_keys++;
     }
     else if( rc ) { /* insert this key */
@@ -575,32 +641,41 @@ import_one( const char *fname, KBNODE keyblock, int fast,
 
         rc = keydb_locate_writable (hd, NULL);
        if (rc) {
-           log_error (_("no writable keyring found: %s\n"), g10_errstr (rc));
+           log_error (_("no writable keyring found: %s\n"), gpg_strerror (rc));
             keydb_release (hd);
-           return G10ERR_GENERAL;
+           return GPG_ERR_GENERAL;
        }
        if( opt.verbose > 1 )
            log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
-       clean_subkeys(keyblock,keyid);
        rc = keydb_insert_keyblock (hd, keyblock );
         if (rc)
           log_error (_("error writing keyring `%s': %s\n"),
-                      keydb_get_resource_name (hd), g10_errstr(rc));
+                      keydb_get_resource_name (hd), gpg_strerror (rc));
        else
-          revalidation_mark ();
+         {
+           /* This should not be possible since we delete the
+              ownertrust when a key is deleted, but it can happen if
+              the keyring and trustdb are out of sync.  It can also
+              be made to happen with the trusted-key command. */
+
+           clear_ownertrusts (pk);
+           if(non_self)
+             revalidation_mark ();
+         }
         keydb_release (hd);
 
        /* we are ready */
        if( !opt.quiet ) {
-           char *p=get_user_id_native(keyid);
+           char *p=get_user_id_printable (keyid);
            log_info( _("key %08lX: public key \"%s\" imported\n"),
                      (ulong)keyid[1],p);
-           m_free(p);
+           xfree (p);
        }
        if( is_status_enabled() ) {
            char *us = get_long_user_id_string( keyid );
            write_status_text( STATUS_IMPORTED, us );
-           m_free(us);
+           xfree (us);
+            print_import_ok (pk,NULL, 1);
        }
        stats->imported++;
        if( is_RSA( pk->pubkey_algo ) )
@@ -632,14 +707,14 @@ import_one( const char *fname, KBNODE keyblock, int fast,
         }
        if( rc ) {
            log_error (_("key %08lX: can't locate original keyblock: %s\n"),
-                                    (ulong)keyid[1], g10_errstr(rc));
+                                    (ulong)keyid[1], gpg_strerror (rc));
             keydb_release (hd);
            goto leave;
        }
        rc = keydb_get_keyblock (hd, &keyblock_orig );
        if (rc) {
            log_error (_("key %08lX: can't read original keyblock: %s\n"),
-                                           (ulong)keyid[1], g10_errstr(rc));
+                                           (ulong)keyid[1], gpg_strerror (rc));
             keydb_release (hd);
            goto leave;
        }
@@ -658,17 +733,16 @@ import_one( const char *fname, KBNODE keyblock, int fast,
        if( n_uids || n_sigs || n_subk ) {
            mod_key = 1;
            /* keyblock_orig has been updated; write */
-           n_sigs-=clean_subkeys(keyblock_orig,keyid);
            rc = keydb_update_keyblock (hd, keyblock_orig);
             if (rc)
                log_error (_("error writing keyring `%s': %s\n"),
-                            keydb_get_resource_name (hd), g10_errstr(rc) );
-           else
+                            keydb_get_resource_name (hd), gpg_strerror (rc) );
+           else if(non_self)
              revalidation_mark ();
 
            /* we are ready */
            if( !opt.quiet ) {
-               char *p=get_user_id_native(keyid);
+               char *p=get_user_id_printable(keyid);
                if( n_uids == 1 )
                    log_info( _("key %08lX: \"%s\" 1 new user ID\n"),
                                             (ulong)keyid[1], p);
@@ -687,19 +761,26 @@ import_one( const char *fname, KBNODE keyblock, int fast,
                else if( n_subk )
                    log_info( _("key %08lX: \"%s\" %d new subkeys\n"),
                                             (ulong)keyid[1], p, n_subk );
-               m_free(p);
+               xfree (p);
            }
 
            stats->n_uids +=n_uids;
            stats->n_sigs +=n_sigs;
            stats->n_subk +=n_subk;
+
+            if (is_status_enabled ()) 
+                 print_import_ok (pk, NULL,
+                                  ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
        }
        else {
+             if (is_status_enabled ()) 
+                  print_import_ok (pk, NULL, 0);
+       
            if( !opt.quiet ) {
-               char *p=get_user_id_native(keyid);
+               char *p=get_user_id_printable(keyid);
                log_info( _("key %08lX: \"%s\" not changed\n"),
                          (ulong)keyid[1],p);
-               m_free(p);
+               xfree (p);
            }
            stats->unchanged++;
        }
@@ -715,6 +796,65 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     return rc;
 }
 
+/* Walk a secret keyblock and produce a public keyblock out of it. */
+static KBNODE
+sec_to_pub_keyblock(KBNODE sec_keyblock)
+{
+  KBNODE secnode,pub_keyblock=NULL,ctx=NULL;
+
+  while((secnode=walk_kbnode(sec_keyblock,&ctx,0)))
+    {
+      KBNODE pubnode;
+
+      if(secnode->pkt->pkttype==PKT_SECRET_KEY ||
+        secnode->pkt->pkttype==PKT_SECRET_SUBKEY)
+       {
+         /* Make a public key.  We only need to convert enough to
+            write the keyblock out. */
+
+         PKT_secret_key *sk=secnode->pkt->pkt.secret_key;
+         PACKET *pkt=xcalloc (1,sizeof(PACKET));
+         PKT_public_key *pk=xcalloc (1,sizeof(PKT_public_key));
+         int n;
+
+         if(secnode->pkt->pkttype==PKT_SECRET_KEY)
+           pkt->pkttype=PKT_PUBLIC_KEY;
+         else
+           pkt->pkttype=PKT_PUBLIC_SUBKEY;
+
+         pkt->pkt.public_key=pk;
+
+         pk->version=sk->version;
+         pk->timestamp=sk->timestamp;
+         pk->expiredate=sk->expiredate;
+         pk->pubkey_algo=sk->pubkey_algo;
+
+         n=pubkey_get_npkey(pk->pubkey_algo);
+         if(n==0)
+           pk->pkey[0]=mpi_copy(sk->skey[0]);
+         else
+           {
+             int i;
+
+             for(i=0;i<n;i++)
+               pk->pkey[i]=mpi_copy(sk->skey[i]);
+           }
+
+         pubnode=new_kbnode(pkt);
+       }
+      else
+       {
+         pubnode=clone_kbnode(secnode);
+       }
+
+      if(pub_keyblock==NULL)
+       pub_keyblock=pubnode;
+      else
+       add_kbnode(pub_keyblock,pubnode);
+    }
+
+  return pub_keyblock;
+}
 
 /****************
  * Ditto for secret keys.  Handling is simpler than for public keys.
@@ -724,7 +864,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
  */
 static int
 import_secret_one( const char *fname, KBNODE keyblock, 
-                   struct stats_s *stats)
+                   struct stats_s *stats, unsigned int options)
 {
     PKT_secret_key *sk;
     KBNODE node, uidnode;
@@ -757,38 +897,63 @@ import_secret_one( const char *fname, KBNODE keyblock,
        return 0;
     }
 
+    if(sk->protect.algo>110)
+      {
+       log_error(_("key %08lX: secret key with invalid cipher %d "
+                   "- skipped\n"),(ulong)keyid[1],sk->protect.algo);
+       return 0;
+      }
+
     clear_kbnode_flags( keyblock );
 
     /* do we have this key already in one of our secrings ? */
     rc = seckey_available( keyid );
-    if( rc == G10ERR_NO_SECKEY && !opt.merge_only ) { /* simply insert this key */
+    if( gpg_err_code (rc) == GPG_ERR_NO_SECKEY && !opt.merge_only ) { 
+        /* simply insert this key */
         KEYDB_HANDLE hd = keydb_new (1);
 
        /* get default resource */
         rc = keydb_locate_writable (hd, NULL);
        if (rc) {
-           log_error (_("no default secret keyring: %s\n"), g10_errstr (rc));
+           log_error (_("no default secret keyring: %s\n"), gpg_strerror (rc));
             keydb_release (hd);
-           return G10ERR_GENERAL;
+           return GPG_ERR_GENERAL;
        }
        rc = keydb_insert_keyblock (hd, keyblock );
         if (rc)
            log_error (_("error writing keyring `%s': %s\n"),
-                       keydb_get_resource_name (hd), g10_errstr(rc) );
+                       keydb_get_resource_name (hd), gpg_strerror (rc) );
         keydb_release (hd);
        /* we are ready */
        if( !opt.quiet )
            log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]);
        stats->secret_imported++;
+        if (is_status_enabled ()) 
+             print_import_ok (NULL, sk, 1|16);
+
+       if(options&IMPORT_SK2PK)
+         {
+           /* Try and make a public key out of this. */
+
+           KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
+           import_one(fname,pub_keyblock,stats,opt.import_options);
+           release_kbnode(pub_keyblock);
+         }
+
     }
     else if( !rc ) { /* we can't merge secret keys */
        log_error( _("key %08lX: already in secret keyring\n"),
                                                        (ulong)keyid[1]);
        stats->secret_dups++;
+        if (is_status_enabled ()) 
+             print_import_ok (NULL, sk, 16);
+
+       /* TODO: if we ever do merge secret keys, make sure to handle
+          the sec_to_pub_keyblock feature as well. */
     }
     else
        log_error( _("key %08lX: secret key not found: %s\n"),
-                               (ulong)keyid[1], g10_errstr(rc));
+                               (ulong)keyid[1], gpg_strerror (rc));
 
     return rc;
 }
@@ -813,17 +978,17 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     keyid[0] = node->pkt->pkt.signature->keyid[0];
     keyid[1] = node->pkt->pkt.signature->keyid[1];
 
-    pk = m_alloc_clear( sizeof *pk );
+    pk = xcalloc (1, sizeof *pk );
     rc = get_pubkey( pk, keyid );
-    if( rc == G10ERR_NO_PUBKEY ) {
-       log_info( _("key %08lX: no public key - "
-                "can't apply revocation certificate\n"), (ulong)keyid[1]);
+    if( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) {
+       log_error ( _("key %08lX: no public key - "
+                      "can't apply revocation certificate\n"), (ulong)keyid[1]);
        rc = 0;
        goto leave;
     }
     else if( rc ) {
        log_error( _("key %08lX: public key not found: %s\n"),
-                                      (ulong)keyid[1], g10_errstr(rc));
+                                      (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
 
@@ -840,13 +1005,13 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     }
     if (rc) {
        log_error (_("key %08lX: can't locate original keyblock: %s\n"),
-                   (ulong)keyid[1], g10_errstr(rc));
+                   (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
     rc = keydb_get_keyblock (hd, &keyblock );
     if (rc) {
        log_error (_("key %08lX: can't read original keyblock: %s\n"),
-                   (ulong)keyid[1], g10_errstr(rc));
+                   (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
 
@@ -857,7 +1022,7 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     rc = check_key_signature( keyblock, node, NULL);
     if( rc ) {
        log_error( _("key %08lX: invalid revocation certificate"
-                 ": %s - rejected\n"), (ulong)keyid[1], g10_errstr(rc));
+                 ": %s - rejected\n"), (ulong)keyid[1], gpg_strerror (rc));
        goto leave;
     }
 
@@ -867,12 +1032,12 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
        if( onode->pkt->pkttype == PKT_USER_ID )
            break;
        else if( onode->pkt->pkttype == PKT_SIGNATURE
-                && onode->pkt->pkt.signature->sig_class == 0x20
-                && keyid[0] == onode->pkt->pkt.signature->keyid[0]
-                && keyid[1] == onode->pkt->pkt.signature->keyid[1] ) {
-           rc = 0;
-           goto leave; /* yes, we already know about it */
-       }
+                 && !cmp_signatures(node->pkt->pkt.signature,
+                                    onode->pkt->pkt.signature))
+          {
+            rc = 0;
+            goto leave; /* yes, we already know about it */
+          }
     }
 
 
@@ -883,16 +1048,23 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     rc = keydb_update_keyblock (hd, keyblock );
     if (rc)
        log_error (_("error writing keyring `%s': %s\n"),
-                   keydb_get_resource_name (hd), g10_errstr(rc) );
+                   keydb_get_resource_name (hd), gpg_strerror (rc) );
     keydb_release (hd); hd = NULL;
     /* we are ready */
     if( !opt.quiet ) {
-        char *p=get_user_id_native(keyid);
+        char *p=get_user_id_printable (keyid);
        log_info( _("key %08lX: \"%s\" revocation certificate imported\n"),
                                        (ulong)keyid[1],p);
-       m_free(p);
+       xfree (p);
     }
     stats->n_revoc++;
+
+    /* If the key we just revoked was ultimately trusted, remove its
+       ultimate trust.  This doesn't stop the user from putting the
+       ultimate trust back, but is a reasonable solution for now. */
+    if(get_ownertrust(pk)==TRUST_ULTIMATE)
+      clear_ownertrusts(pk);
+
     revalidation_mark ();
 
   leave:
@@ -907,21 +1079,41 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
  * loop over the keyblock and check all self signatures.
  * Mark all user-ids with a self-signature by setting flag bit 0.
  * Mark all user-ids with an invalid self-signature by setting bit 1.
- * This works also for subkeys, here the subkey is marked.
+ * This works also for subkeys, here the subkey is marked.  Invalid or
+ * extra subkey sigs (binding or revocation) are marked for deletion.
+ * non_self is set to true if there are any sigs other than self-sigs
+ * in this keyblock.
  */
 static int
 chk_self_sigs( const char *fname, KBNODE keyblock,
-              PKT_public_key *pk, u32 *keyid )
+              PKT_public_key *pk, u32 *keyid, int *non_self )
 {
-    KBNODE n;
+    KBNODE n,knode=NULL;
     PKT_signature *sig;
     int rc;
+    u32 bsdate=0,rsdate=0;
+    KBNODE bsnode=NULL,rsnode=NULL;
 
     for( n=keyblock; (n = find_next_kbnode(n, 0)); ) {
-       if( n->pkt->pkttype != PKT_SIGNATURE )
+      if(n->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+       {
+         knode=n;
+         bsdate=0;
+         rsdate=0;
+         bsnode=NULL;
+         rsnode=NULL;
+         continue;
+       }
+      else if( n->pkt->pkttype != PKT_SIGNATURE )
            continue;
        sig = n->pkt->pkt.signature;
        if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
+
+         /* This just caches the sigs for later use.  That way we
+            import a fully-cached key which speeds things up. */
+         if(!opt.no_sig_cache)
+           check_key_signature(keyblock,n,NULL);
+
            if( (sig->sig_class&~3) == 0x10 ) {
                KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
                if( !unode )  {
@@ -935,48 +1127,121 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                  rc = check_key_signature( keyblock, n, NULL);
                  if( rc )
                    {
-                     char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
-                                     strlen(unode->pkt->pkt.user_id->name),0);
-                     log_info( rc == G10ERR_PUBKEY_ALGO ?
-                               _("key %08lX: unsupported public key "
-                                 "algorithm on user id \"%s\"\n"):
-                               _("key %08lX: invalid self-signature "
-                                 "on user id \"%s\"\n"),
-                               (ulong)keyid[1],p);
-                     m_free(p);
-                   }
-                 else
+                      if (opt.verbose)
+                        {
+                          char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+                                       strlen(unode->pkt->pkt.user_id->name),0);
+                          log_info( gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
+                                    _("key %08lX: unsupported public key "
+                                      "algorithm on user id \"%s\"\n"):
+                                    _("key %08lX: invalid self-signature "
+                                      "on user id \"%s\"\n"),
+                                    (ulong)keyid[1],p);
+                          xfree (p);
+                        }
+                    }
+                  else
                    unode->flag |= 1; /* mark that signature checked */
                }
            }
            else if( sig->sig_class == 0x18 ) {
-               KBNODE knode = find_prev_kbnode( keyblock,
-                                                n, PKT_PUBLIC_SUBKEY );
-               if( !knode )
-                   knode = find_prev_kbnode( keyblock,
-                                                n, PKT_SECRET_SUBKEY );
+             /* Note that this works based solely on the timestamps
+                like the rest of gpg.  If the standard gets
+                revocation targets, this may need to be revised. */
 
+               if( !knode )
+                  {
+                    if (opt.verbose)
+                      log_info( _("key %08lX: no subkey for subkey "
+                                  "binding signature\n"),(ulong)keyid[1]);
+                    n->flag |= 4; /* delete this */
+                  }
+               else
+                  {
+                    rc = check_key_signature( keyblock, n, NULL);
+                    if( rc )
+                      {
+                        if (opt.verbose)
+                          log_info(  gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
+                            _("key %08lX: unsupported public key algorithm\n"):
+                           _("key %08lX: invalid subkey binding\n"),
+                                     (ulong)keyid[1]);
+                        n->flag|=4;
+                      }
+                    else
+                      {
+                        /* It's valid, so is it newer? */
+                        if(sig->timestamp>=bsdate) 
+                          {
+                            knode->flag |= 1;  /* the subkey is valid */
+                            if(bsnode)
+                              {
+                                bsnode->flag|=4; /* Delete the last binding
+                                                    sig since this one is
+                                                    newer */
+                                if (opt.verbose)
+                                  log_info(_("key %08lX: removed multiple "
+                                             "subkey binding\n"),
+                                           (ulong)keyid[1]);
+                              }
+                            
+                            bsnode=n;
+                            bsdate=sig->timestamp;
+                          }
+                        else
+                          n->flag|=4; /* older */
+                      }
+                  }
+           }
+           else if( sig->sig_class == 0x28 ) {
+             /* We don't actually mark the subkey as revoked right
+                 now, so just check that the revocation sig is the
+                 most recent valid one.  Note that we don't care if
+                 the binding sig is newer than the revocation sig.
+                 See the comment in getkey.c:merge_selfsigs_subkey for
+                 more */
                if( !knode ) {
-                   log_info( _("key %08lX: no subkey for key binding\n"),
-                                           (ulong)keyid[1]);
-                   n->flag |= 4; /* delete this */
+                  if (opt.verbose)
+                   log_info( _("key %08lX: no subkey for subkey "
+                               "revocation signature\n"),(ulong)keyid[1]);
+                  n->flag |= 4; /* delete this */
                }
                else {
-                 /* If it hasn't been marked valid yet, keep trying */
-                 if(!(knode->flag&1)) {
-                   rc = check_key_signature( keyblock, n, NULL);
-                   if( rc )
-                       log_info(  rc == G10ERR_PUBKEY_ALGO ?
-                          _("key %08lX: unsupported public key algorithm\n"):
-                          _("key %08lX: invalid subkey binding\n"),
-                                        (ulong)keyid[1]);
+                 rc = check_key_signature( keyblock, n, NULL);
+                 if( rc ) {
+                    if (opt.verbose)
+                      log_info(  gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ?
+                           _("key %08lX: unsupported public key algorithm\n"):
+                           _("key %08lX: invalid subkey revocation\n"),
+                              (ulong)keyid[1]);
+                   n->flag|=4;
+                 }
+                 else {
+                   /* It's valid, so is it newer? */
+                   if(sig->timestamp>=rsdate) {
+                     if(rsnode) {
+                       rsnode->flag|=4; /* Delete the last revocation
+                                           sig since this one is
+                                           newer */
+                        if (opt.verbose)
+                          log_info(_("key %08lX: removed multiple subkey "
+                                     "revocation signatures\n"),
+                                   (ulong)keyid[1]);
+                     }
+
+                     rsnode=n;
+                     rsdate=sig->timestamp;
+                   }
                    else
-                     knode->flag |= 1; /* mark that signature checked */
+                     n->flag|=4; /* older */
                  }
                }
            }
        }
+       else
+         *non_self=1;
     }
+
     return 0;
 }
 
@@ -988,10 +1253,11 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
  * returns: true if at least one valid user-id is left over.
  */
 static int
-delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
+delete_inv_parts( const char *fname, KBNODE keyblock,
+                 u32 *keyid, unsigned int options)
 {
     KBNODE node;
-    int nvalid=0, uid_seen=0;
+    int nvalid=0, uid_seen=0, subkey_seen=0;
 
     for(node=keyblock->next; node; node = node->next ) {
        if( node->pkt->pkttype == PKT_USER_ID ) {
@@ -1032,31 +1298,37 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
                    node = node->next;
                }
            }
+           else
+             subkey_seen = 1;
        }
        else if( node->pkt->pkttype == PKT_SIGNATURE
-                && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo)
+                && openpgp_pk_test_algo( node->pkt->pkt.signature
+                                                     ->pubkey_algo, 0)
                 && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
            delete_kbnode( node ); /* build_packet() can't handle this */
        else if( node->pkt->pkttype == PKT_SIGNATURE &&
                 !node->pkt->pkt.signature->flags.exportable &&
+                !(options&IMPORT_ALLOW_LOCAL_SIGS) &&
                 seckey_available( node->pkt->pkt.signature->keyid ) ) {
-           /* here we violate the rfc a bit by still allowing
+           /* Here we violate the rfc a bit by still allowing
             * to import non-exportable signature when we have the
             * the secret key used to create this signature - it
-            * seems that this makes sense */
+            * seems that this makes sense. */
+          if (opt.verbose)
            log_info( _("key %08lX: non exportable signature "
                                    "(class %02x) - skipped\n"),
-                                   (ulong)keyid[1],
+                      (ulong)keyid[1],
                                     node->pkt->pkt.signature->sig_class );
-           delete_kbnode( node );
+          delete_kbnode( node );
        }
        else if( node->pkt->pkttype == PKT_SIGNATURE
                 && node->pkt->pkt.signature->sig_class == 0x20 )  {
            if( uid_seen ) {
+              if (opt.verbose)
                log_error( _("key %08lX: revocation certificate "
-                                    "at wrong place - skipped\n"),
+                             "at wrong place - skipped\n"),
                                    (ulong)keyid[1]);
-               delete_kbnode( node );
+              delete_kbnode( node );
            }
            else {
              /* If the revocation cert is from a different key than
@@ -1070,14 +1342,34 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
                  int rc = check_key_signature( keyblock, node, NULL);
                  if( rc )
                    {
-                     log_error( _("key %08lX: invalid revocation "
-                                  "certificate: %s - skipped\n"),
-                                (ulong)keyid[1], g10_errstr(rc));
+                      if (opt.verbose)
+                        log_info ( _("key %08lX: invalid revocation "
+                                     "certificate: %s - skipped\n"),
+                                   (ulong)keyid[1], gpg_strerror (rc));
                      delete_kbnode( node );
                    }
                }
            }
        }
+       else if( node->pkt->pkttype == PKT_SIGNATURE &&
+                (node->pkt->pkt.signature->sig_class == 0x18 ||
+                 node->pkt->pkt.signature->sig_class == 0x28) &&
+                !subkey_seen ) {
+          if (opt.verbose)
+           log_info ( _("key %08lX: subkey signature "
+                        "in wrong place - skipped\n"),
+                      (ulong)keyid[1]);
+          delete_kbnode( node );
+       }
+       else if( node->pkt->pkttype == PKT_SIGNATURE
+                && !IS_CERT(node->pkt->pkt.signature))
+         {
+            if (opt.verbose)
+              log_info (_("key %08lX: unexpected signature class (0x%02X) -"
+                          " skipped\n"),(ulong)keyid[1],
+                        node->pkt->pkt.signature->sig_class);
+           delete_kbnode(node);
+         }
        else if( (node->flag & 4) ) /* marked for deletion */
            delete_kbnode( node );
     }
@@ -1171,8 +1463,9 @@ collapse_uids( KBNODE *keyblock )
        kid1 = keyid_from_sk( n->pkt->pkt.secret_key, NULL );
     else
        kid1 = 0;
-    log_info(_("key %08lX: duplicated user ID detected - merged\n"),
-                                                                (ulong)kid1);
+    if (!opt.quiet)
+      log_info (_("key %08lX: duplicated user ID detected - merged\n"),
+                (ulong)kid1);
 
     return 1;
 }
@@ -1222,15 +1515,16 @@ revocation_present(KBNODE keyblock)
                          itself? */
                      int rc;
 
-                     rc=get_pubkey_byfprint(NULL,sig->revkey[idx]->fpr,
-                                            MAX_FINGERPRINT_LEN);
-                     if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
+                     rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr,
+                                                   MAX_FINGERPRINT_LEN);
+                     if ( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
+                          || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY)
                        {
                          /* No, so try and get it */
                          if(opt.keyserver_scheme &&
                             opt.keyserver_options.auto_key_retrieve)
                            {
-                             log_info(_("Warning: key %08lX may be revoked: "
+                             log_info(_("WARNING: key %08lX may be revoked: "
                                         "fetching revocation key %08lX\n"),
                                       (ulong)keyid_from_pk(pk,NULL),
                                       (ulong)keyid[1]);
@@ -1238,13 +1532,14 @@ revocation_present(KBNODE keyblock)
                                                      MAX_FINGERPRINT_LEN);
 
                              /* Do we have it now? */
-                             rc=get_pubkey_byfprint(NULL,
+                             rc=get_pubkey_byfprint_fast (NULL,
                                                     sig->revkey[idx]->fpr,
                                                     MAX_FINGERPRINT_LEN);
                            }
 
-                         if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
-                           log_info(_("Warning: key %08lX may be revoked: "
+                         if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
+                              || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY)
+                           log_info(_("WARNING: key %08lX may be revoked: "
                                       "revocation key %08lX not present.\n"),
                                     (ulong)keyid_from_pk(pk,NULL),
                                     (ulong)keyid[1]);
@@ -1287,23 +1582,27 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                    break;
                else if( onode->pkt->pkttype == PKT_SIGNATURE
                         && onode->pkt->pkt.signature->sig_class == 0x20
-                        && node->pkt->pkt.signature->keyid[0]
-                           == onode->pkt->pkt.signature->keyid[0]
-                        && node->pkt->pkt.signature->keyid[1]
-                           == onode->pkt->pkt.signature->keyid[1] ) {
-                   found = 1;
-                   break;
-               }
+                        && !cmp_signatures(onode->pkt->pkt.signature,
+                                           node->pkt->pkt.signature))
+                  {
+                    found = 1;
+                    break;
+                  }
            }
            if( !found ) {
-               char *p=get_user_id_native(keyid);
                KBNODE n2 = clone_kbnode(node);
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
                 ++*n_sigs;
-               log_info(_("key %08lX: \"%s\" revocation certificate added\n"),
-                                        (ulong)keyid[1],p);
-               m_free(p);
+
+                if (!opt.quiet)
+                  {
+                    char *p=get_user_id_printable (keyid);
+                    log_info(_("key %08lX: \"%s\" "
+                               "revocation certificate added\n"),
+                             (ulong)keyid[1],p);
+                    xfree (p);
+                  }
            }
        }
     }
@@ -1332,8 +1631,9 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
                 ++*n_sigs;
-               log_info( _("key %08lX: direct key signature added\n"),
-                                        (ulong)keyid[1]);
+                if (!opt.quiet)
+                  log_info( _("key %08lX: direct key signature added\n"),
+                            (ulong)keyid[1]);
            }
        }
     }
@@ -1501,20 +1801,12 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
            || n->pkt->pkt.signature->sig_class == 0x28 )
            continue; /* skip signatures which are only valid on subkeys */
        found = 0;
-       for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next){
-           if( n2->pkt->pkttype == PKT_SIGNATURE
-               && n->pkt->pkt.signature->keyid[0]
-                  == n2->pkt->pkt.signature->keyid[0]
-               && n->pkt->pkt.signature->keyid[1]
-                  == n2->pkt->pkt.signature->keyid[1]
-               && n->pkt->pkt.signature->timestamp
-                  <= n2->pkt->pkt.signature->timestamp
-               && n->pkt->pkt.signature->sig_class
-                  == n2->pkt->pkt.signature->sig_class ) {
-               found++;
-               break;
-           }
-       }
+       for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next)
+         if(!cmp_signatures(n->pkt->pkt.signature,n2->pkt->pkt.signature))
+           {
+             found++;
+             break;
+           }   
        if( !found ) {
            /* This signature is new or newer, append N to DST.
             * We add a clone to the original keyblock, because this