* options.h, g10.c (main), import.c (parse_import_options, import_one,
[gnupg.git] / g10 / import.c
index 3d5cd2a..2168fe5 100644 (file)
@@ -1,14 +1,15 @@
 /* import.c
- *     Copyright (C) 1998 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ *               2003 Free Software Foundation, Inc.
  *
- * This file is part of GNUPG.
+ * This file is part of GnuPG.
  *
- * GNUPG is free software; you can redistribute it and/or modify
+ * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * GNUPG is distributed in the hope that it will be useful,
+ * GnuPG is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
 #include "util.h"
 #include "trustdb.h"
 #include "main.h"
-
-
-static int read_block( IOBUF a, compress_filter_context_t *cfx,
-                            PACKET **pending_pkt, KBNODE *ret_root );
-static int import_one( const char *fname, KBNODE keyblock );
-static int import_revoke_cert( const char *fname, KBNODE node );
+#include "i18n.h"
+#include "ttyio.h"
+#include "status.h"
+#include "keyserver-internal.h"
+
+struct stats_s {
+    ulong count;
+    ulong no_user_id;
+    ulong imported;
+    ulong imported_rsa;
+    ulong n_uids;
+    ulong n_sigs;
+    ulong n_subk;
+    ulong unchanged;
+    ulong n_revoc;
+    ulong secret_read;
+    ulong secret_imported;
+    ulong secret_dups;
+    ulong skipped_new_keys;
+    ulong not_imported;
+};
+
+
+static int import( IOBUF inp, const char* fname,
+                   struct stats_s *stats, unsigned int options );
+static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
+static void revocation_present(KBNODE keyblock);
+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, 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_cert *pkc, 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 );
+                        KBNODE keyblock, u32 *keyid,
+                        int *n_uids, int *n_sigs, int *n_subk );
 static int append_uid( KBNODE keyblock, KBNODE node, int *n_sigs,
                             const char *fname, u32 *keyid );
+static int append_key( KBNODE keyblock, KBNODE node, int *n_sigs,
+                            const char *fname, u32 *keyid );
 static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
                             const char *fname, u32 *keyid );
+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,int noisy)
+{
+  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},
+      {"merge-only",IMPORT_MERGE_ONLY},
+      {NULL,0}
+    };
+
+  return parse_options(str,options,import_opts,noisy);
+}
+
+void *
+import_new_stats_handle (void)
+{
+    return m_alloc_clear ( sizeof (struct stats_s) );
+}
 
+void
+import_release_stats_handle (void *p)
+{
+    m_free (p);
+}
 
 /****************
  * Import the public keys from the given filename. Input may be armored.
- * This function rejects alls keys which are not valid self signed on at
+ * This function rejects all keys which are not validly self signed on at
  * least one userid. Only user ids which are self signed will be imported.
- * Other signatures are not not checked.
+ * Other signatures are not checked.
  *
- * Actually this functtion does a merge. It works like this:
+ * Actually this function does a merge. It works like this:
  *
  *  - get the keyblock
  *  - check self-signatures and remove all userids and their signatures
  *    without/invalid self-signatures.
  *  - reject the keyblock, if we have no valid userid.
- *  - See wether we have this key already in one of our pubrings.
+ *  - See whether we have this key already in one of our pubrings.
  *    If not, simply add it to the default keyring.
  *  - Compare the key and the self-signatures of the new and the one in
- *    our keyring.  If they are differen something weird is going on;
+ *    our keyring.  If they are different something weird is going on;
  *    ask what to do.
- *  - See wether we have only non-self-signature on one user id; if not
+ *  - See whether we have only non-self-signature on one user id; if not
  *    ask the user what to do.
  *  - compare the signatures: If we already have this signature, check
  *    that they compare okay; if not, issue a warning and ask the user.
- *    (consider to look at the timestamp and use the newest?)
+ *    (consider looking at the timestamp and use the newest?)
  *  - Simply add the signature.  Can't verify here because we may not have
- *    the signatures public key yet; verification is done when putting it
+ *    the signature's public key yet; verification is done when putting it
  *    into the trustdb, which is done automagically as soon as this pubkey
  *    is used.
  *  - Proceed with next signature.
  *
- *  Key revocation certificates have special handling.
+ *  Key revocation certificates have special handling.
  *
  */
+static int
+import_keys_internal( IOBUF inp, char **fnames, int nnames,
+                     void *stats_handle, unsigned int options )
+{
+    int i, rc = 0;
+    struct stats_s *stats = stats_handle;
+
+    if (!stats)
+        stats = import_new_stats_handle ();
+
+    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 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,
+                                     g10_errstr(rc) );
+           }
+           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;
+}
+
+void
+import_keys( char **fnames, int nnames,
+            void *stats_handle, unsigned int options )
+{
+    import_keys_internal( NULL, fnames, nnames, stats_handle, options);
+}
+
 int
-import_pubkeys( const char *fname )
+import_keys_stream( IOBUF inp, void *stats_handle, unsigned int options )
+{
+    return import_keys_internal( inp, NULL, 0, stats_handle, options);
+}
+
+static int
+import( IOBUF inp, const char* fname,
+       struct stats_s *stats, unsigned int options )
 {
-    armor_filter_context_t afx;
-    compress_filter_context_t cfx;
     PACKET *pending_pkt = NULL;
-    IOBUF inp = NULL;
     KBNODE keyblock;
     int rc = 0;
 
-    memset( &afx, 0, sizeof afx);
-    memset( &cfx, 0, sizeof cfx);
+    getkey_disable_caches();
 
-    /* open file */
-    inp = iobuf_open(fname);
-    if( !fname )
-       fname = "[stdin]";
-    if( !inp ) {
-       log_error("%s: can't open file: %s\n", fname, strerror(errno) );
-       return G10ERR_OPEN_FILE;
+    if( !opt.no_armor ) { /* armored reading is not disabled */
+       armor_filter_context_t *afx = m_alloc_clear( sizeof *afx );
+       afx->only_keyblocks = 1;
+       iobuf_push_filter2( inp, armor_filter, afx, 1 );
     }
 
-    if( !opt.no_armor ) /* armored reading is not diabled */
-       iobuf_push_filter( inp, armor_filter, &afx );
-
-    while( !(rc = read_block( inp, &cfx, &pending_pkt, &keyblock) )) {
-       if( keyblock->pkt->pkttype == PKT_PUBLIC_CERT )
-           rc = import_one( fname, keyblock );
+    while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
+       if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
+           rc = import_one( fname, keyblock, stats, options );
+       else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
+                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 );
-       else
-           log_info("%s: skipping block of type %d\n",
-                                           fname, keyblock->pkt->pkttype );
+           rc = import_revoke_cert( fname, keyblock, stats );
+       else {
+           log_info( _("skipping block of type %d\n"),
+                                           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 processed so far\n"), stats->count );
     }
     if( rc == -1 )
        rc = 0;
-    else if( rc )
-       log_error("%s: read error: %s\n", fname, g10_errstr(rc));
+    else if( rc && rc != G10ERR_INV_KEYRING )
+       log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc));
 
-    iobuf_close(inp);
     return rc;
 }
 
 
+void
+import_print_stats (void *hd)
+{
+    struct stats_s *stats = hd;
+
+    if( !opt.quiet ) {
+       log_info(_("Total number processed: %lu\n"), stats->count );
+       if( stats->skipped_new_keys )
+           log_info(_("      skipped new keys: %lu\n"),
+                                               stats->skipped_new_keys );
+       if( stats->no_user_id )
+           log_info(_("          w/o user IDs: %lu\n"), stats->no_user_id );
+       if( stats->imported || stats->imported_rsa ) {
+           log_info(_("              imported: %lu"), stats->imported );
+           if( stats->imported_rsa )
+               fprintf(stderr, "  (RSA: %lu)", stats->imported_rsa );
+           putc('\n', stderr);
+       }
+       if( stats->unchanged )
+           log_info(_("             unchanged: %lu\n"), stats->unchanged );
+       if( stats->n_uids )
+           log_info(_("          new user IDs: %lu\n"), stats->n_uids );
+       if( stats->n_subk )
+           log_info(_("           new subkeys: %lu\n"), stats->n_subk );
+       if( stats->n_sigs )
+           log_info(_("        new signatures: %lu\n"), stats->n_sigs );
+       if( stats->n_revoc )
+           log_info(_("   new key revocations: %lu\n"), stats->n_revoc );
+       if( stats->secret_read )
+           log_info(_("      secret keys read: %lu\n"), stats->secret_read );
+       if( stats->secret_imported )
+           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[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,
+               stats->imported_rsa,
+               stats->unchanged,
+               stats->n_uids,
+               stats->n_subk,
+               stats->n_sigs,
+               stats->n_revoc,
+               stats->secret_read,
+               stats->secret_imported,
+               stats->secret_dups,
+               stats->skipped_new_keys,
+                stats->not_imported );
+       write_status_text( STATUS_IMPORT_RES, buf );
+    }
+}
+
+
 /****************
- * Read the next keyblock from stream A, CFX is used to handle
- * compressed keyblocks. PENDING_PKT should be initialzed to NULL
+ * Read the next keyblock from stream A.
+ * PENDING_PKT should be initialzed to NULL
  * and not chnaged form the caller.
  * Retunr: 0 = okay, -1 no more blocks or another errorcode.
  */
 static int
-read_block( IOBUF a, compress_filter_context_t *cfx,
-           PACKET **pending_pkt, KBNODE *ret_root )
+read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
 {
     int rc;
     PACKET *pkt;
@@ -177,21 +365,29 @@ read_block( IOBUF a, compress_filter_context_t *cfx,
        /* make a linked list of all packets */
        switch( pkt->pkttype ) {
          case PKT_COMPRESSED:
-           if( pkt->pkt.compressed->algorithm == 1 )
-               cfx->pgpmode = 1;
-           else if( pkt->pkt.compressed->algorithm != 2  ){
+           if(check_compress_algo(pkt->pkt.compressed->algorithm))
+             {
                rc = G10ERR_COMPR_ALGO;
                goto ready;
-           }
-           pkt->pkt.compressed->buf = NULL;
-           iobuf_push_filter( a, compress_filter, cfx );
+             }
+           else
+             {
+               compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx );
+               pkt->pkt.compressed->buf = NULL;
+               push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1);
+             }
            free_packet( pkt );
            init_packet(pkt);
            break;
 
+          case PKT_RING_TRUST:
+            /* skip those packets */
+           free_packet( pkt );
+           init_packet(pkt);
+            break;
 
-         case PKT_PUBLIC_CERT:
-         case PKT_SECRET_CERT:
+         case PKT_PUBLIC_KEY:
+         case PKT_SECRET_KEY:
            if( in_cert ) { /* store this packet */
                *pending_pkt = pkt;
                pkt = NULL;
@@ -223,6 +419,117 @@ read_block( IOBUF a, compress_filter_context_t *cfx,
     return rc;
 }
 
+/* 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
+fix_pks_corruption(KBNODE keyblock)
+{
+  int changed=0,keycount=0;
+  KBNODE node,last=NULL,sknode=NULL;
+
+  /* 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)
+    {
+      if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+       {
+         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. */
+
+         /* Sanity check */
+         if(last==NULL)
+           break;
+
+         /* Temporarily attach node to sknode. */
+         node->next=sknode->next;
+         sknode->next=node;
+         last->next=NULL;
+
+         /* 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))
+           {
+             /* Not a match, so undo the changes. */
+             sknode->next=node->next;
+             last->next=node;
+             node->next=NULL;
+             break;
+           }
+         else
+           {
+             sknode->flag |= 1; /* Mark it good so we don't need to
+                                    check it again */
+             changed=1;
+             break;
+           }
+       }
+      else
+       keycount=0;
+    }
+
+  return changed;
+}
+
+
+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 = m_alloc (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);
+    m_free (buf);
+}
 
 /****************
  * Try to import one keyblock. Return an error only in serious cases, but
@@ -231,160 +538,434 @@ read_block( IOBUF a, compress_filter_context_t *cfx,
  * which called g10.
  */
 static int
-import_one( const char *fname, KBNODE keyblock )
+import_one( const char *fname, KBNODE keyblock,
+            struct stats_s *stats, unsigned int options )
 {
-    PKT_public_cert *pkc;
-    PKT_public_cert *pkc_orig;
+    PKT_public_key *pk;
+    PKT_public_key *pk_orig;
     KBNODE node, uidnode;
     KBNODE keyblock_orig = NULL;
-    KBPOS kbpos;
     u32 keyid[2];
     int rc = 0;
+    int new_key = 0;
+    int mod_key = 0;
+    int non_self = 0;
 
-    /* get the key and print some infos about it */
-    node = find_kbnode( keyblock, PKT_PUBLIC_CERT );
-    if( !node ) {
-       log_error("%s: Oops; public key not found anymore!\n", fname);
-       return G10ERR_GENERAL; /* really serious */
-    }
+    /* get the key and print some info about it */
+    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
+    if( !node )
+       BUG();
 
-    pkc = node->pkt->pkt.public_cert;
-    keyid_from_pkc( pkc, keyid );
+    pk = node->pkt->pkt.public_key;
+    keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if( opt.verbose ) {
-       log_info("%s: pub  %4u%c/%08lX %s   ", fname,
-                 nbits_from_pkc( pkc ),
-                 pubkey_letter( pkc->pubkey_algo ),
-                 (ulong)keyid[1], datestr_from_pkc(pkc) );
+    if( opt.verbose && !opt.interactive ) {
+       log_info( "pub  %4u%c/%08lX %s   ",
+                 nbits_from_pk( pk ),
+                 pubkey_letter( pk->pubkey_algo ),
+                 (ulong)keyid[1], datestr_from_pk(pk) );
        if( uidnode )
-           print_string( stderr, uidnode->pkt->pkt.user_id->name,
-                                 uidnode->pkt->pkt.user_id->len, 0 );
+           print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+                                      uidnode->pkt->pkt.user_id->len );
        putc('\n', stderr);
     }
     if( !uidnode ) {
-       log_error("%s: No user id for key %08lX\n", fname, (ulong)keyid[1]);
+       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 , pkc, 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;
 
-    if( !delete_inv_parts( fname, keyblock, keyid ) ) {
-       log_info("%s: key %08lX, no valid user ids\n",
-                                                   fname, (ulong)keyid[1]);
+    /* If we allow such a thing, mark unsigned uids as valid */
+    if( opt.allow_non_selfsigned_uid )
+      for( node=keyblock; node; node = node->next )
+       if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) )
+         {
+           char *user=utf8_to_native(node->pkt->pkt.user_id->name,
+                                     node->pkt->pkt.user_id->len,0);
+           node->flag |= 1;
+           log_info( _("key %08lX: accepted non self-signed user ID '%s'\n"),
+                     (ulong)keyid[1],user);
+           m_free(user);
+         }
+
+    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 ? */
-    pkc_orig = m_alloc_clear( sizeof *pkc_orig );
-    rc = get_pubkey( pkc_orig, keyid );
-    if( rc && rc != G10ERR_NO_PUBKEY ) {
-       log_error("%s: key %08lX, public key not found: %s\n",
-                               fname, (ulong)keyid[1], g10_errstr(rc));
+    pk_orig = m_alloc_clear( sizeof *pk_orig );
+    rc = get_pubkey_fast ( pk_orig, keyid );
+    if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) {
+       log_error( _("key %08lX: public key not found: %s\n"),
+                               (ulong)keyid[1], g10_errstr(rc));
     }
+    else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) )
+      {
+       if( opt.verbose )
+         log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] );
+       rc = 0;
+       stats->skipped_new_keys++;
+      }
     else if( rc ) { /* insert this key */
-       /* get default resource */
-       if( get_keyblock_handle( NULL, 0, &kbpos ) ) {
-           log_error("no default public keyring\n");
+        KEYDB_HANDLE hd = keydb_new (0);
+
+        rc = keydb_locate_writable (hd, NULL);
+       if (rc) {
+           log_error (_("no writable keyring found: %s\n"), g10_errstr (rc));
+            keydb_release (hd);
            return G10ERR_GENERAL;
        }
        if( opt.verbose > 1 )
-           log_info("%s: writing to '%s'\n",
-                               fname, keyblock_resource_name(&kbpos) );
-       if( (rc=lock_keyblock( &kbpos )) )
-           log_error("can't lock public keyring '%s': %s\n",
-                            keyblock_resource_name(&kbpos), g10_errstr(rc) );
-       else if( (rc=insert_keyblock( &kbpos, keyblock )) )
-           log_error("%s: can't write to '%s': %s\n", fname,
-                            keyblock_resource_name(&kbpos), g10_errstr(rc) );
-       unlock_keyblock( &kbpos );
+           log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
+       rc = keydb_insert_keyblock (hd, keyblock );
+        if (rc)
+          log_error (_("error writing keyring `%s': %s\n"),
+                      keydb_get_resource_name (hd), g10_errstr(rc));
+       else
+         {
+           /* 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 */
-       log_info("%s: key %08lX imported\n", fname, (ulong)keyid[1]);
+       if( !opt.quiet ) {
+           char *p=get_user_id_printable (keyid);
+           log_info( _("key %08lX: public key \"%s\" imported\n"),
+                     (ulong)keyid[1],p);
+           m_free(p);
+       }
+       if( is_status_enabled() ) {
+           char *us = get_long_user_id_string( keyid );
+           write_status_text( STATUS_IMPORTED, us );
+           m_free(us);
+            print_import_ok (pk,NULL, 1);
+       }
+       stats->imported++;
+       if( is_RSA( pk->pubkey_algo ) )
+           stats->imported_rsa++;
+       new_key = 1;
     }
     else { /* merge */
-       int n_uids, n_sigs;
+        KEYDB_HANDLE hd;
+       int n_uids, n_sigs, n_subk;
 
        /* Compare the original against the new key; just to be sure nothing
         * weird is going on */
-       if( cmp_public_certs( pkc_orig, pkc ) ) {
-           log_error("%s: key %08lX, doesn't match our copy\n",
-                                                   fname, (ulong)keyid[1]);
-           rc = G10ERR_GENERAL;
+       if( cmp_public_keys( pk_orig, pk ) ) {
+           log_error( _("key %08lX: doesn't match our copy\n"),
+                                                         (ulong)keyid[1]);
            goto leave;
        }
 
-       /* See wether we have only non-self-signature on one user id; if not
-        * ask the user what to do. <--- fixme */
-
        /* now read the original keyblock */
-       rc = find_keyblock_bypkc( &kbpos, pkc_orig );
+        hd = keydb_new (0);
+        {
+            byte afp[MAX_FINGERPRINT_LEN];
+            size_t an;
+
+            fingerprint_from_pk (pk_orig, afp, &an);
+            while (an < MAX_FINGERPRINT_LEN) 
+                afp[an++] = 0;
+            rc = keydb_search_fpr (hd, afp);
+        }
        if( rc ) {
-           log_error("%s: key %08lX, can't locate original keyblock: %s\n",
-                                    fname, (ulong)keyid[1], g10_errstr(rc));
+           log_error (_("key %08lX: can't locate original keyblock: %s\n"),
+                                    (ulong)keyid[1], g10_errstr(rc));
+            keydb_release (hd);
            goto leave;
        }
-       rc = read_keyblock( &kbpos, &keyblock_orig );
-       if( rc ) {
-           log_error("%s: key %08lX, can't read original keyblock: %s\n",
-                                    fname, (ulong)keyid[1], g10_errstr(rc));
+       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));
+            keydb_release (hd);
            goto leave;
        }
+
+       collapse_uids( &keyblock );
        /* and try to merge the block */
        clear_kbnode_flags( keyblock_orig );
        clear_kbnode_flags( keyblock );
-       n_uids = n_sigs = 0;
+       n_uids = n_sigs = n_subk = 0;
        rc = merge_blocks( fname, keyblock_orig, keyblock,
-                               keyid, &n_uids, &n_sigs );
-       if( rc )
+                               keyid, &n_uids, &n_sigs, &n_subk );
+       if( rc ) {
+            keydb_release (hd);
            goto leave;
-       if( n_uids || n_sigs ) { /* keyblock_orig has been updated; write */
-           if( opt.verbose > 1 )
-               log_info("%s: writing to '%s'\n",
-                                   fname, keyblock_resource_name(&kbpos) );
-           if( (rc=lock_keyblock( &kbpos )) )
-               log_error("can't lock public keyring '%s': %s\n",
-                                keyblock_resource_name(&kbpos), g10_errstr(rc) );
-           else if( (rc=update_keyblock( &kbpos, keyblock )) )
-               log_error("%s: can't write to '%s': %s\n", fname,
-                                keyblock_resource_name(&kbpos), g10_errstr(rc) );
-           unlock_keyblock( &kbpos );
+        }
+       if( n_uids || n_sigs || n_subk ) {
+           mod_key = 1;
+           /* keyblock_orig has been updated; write */
+           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 if(non_self)
+             revalidation_mark ();
+
            /* we are ready */
-           if( n_uids == 1 )
-               log_info("%s: key %08lX, 1 new user-id\n",
-                                        fname, (ulong)keyid[1]);
-           else if( n_uids )
-               log_info("%s: key %08lX, %d new user-ids\n",
-                                        fname, (ulong)keyid[1], n_uids );
-           if( n_sigs == 1 )
-               log_info("%s: key %08lX, 1 new signature\n",
-                                        fname, (ulong)keyid[1]);
-           else if( n_sigs )
-               log_info("%s: key %08lX, %d new signatures\n",
-                                        fname, (ulong)keyid[1], n_sigs );
+           if( !opt.quiet ) {
+               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);
+               else if( n_uids )
+                   log_info( _("key %08lX: \"%s\" %d new user IDs\n"),
+                                            (ulong)keyid[1], p, n_uids );
+               if( n_sigs == 1 )
+                   log_info( _("key %08lX: \"%s\" 1 new signature\n"),
+                                            (ulong)keyid[1], p);
+               else if( n_sigs )
+                   log_info( _("key %08lX: \"%s\" %d new signatures\n"),
+                                            (ulong)keyid[1], p, n_sigs );
+               if( n_subk == 1 )
+                   log_info( _("key %08lX: \"%s\" 1 new subkey\n"),
+                                            (ulong)keyid[1], p);
+               else if( n_subk )
+                   log_info( _("key %08lX: \"%s\" %d new subkeys\n"),
+                                            (ulong)keyid[1], p, n_subk );
+               m_free(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
-           log_info("%s: key %08lX, not changed\n", fname, (ulong)keyid[1] );
+       else {
+             if (is_status_enabled ()) 
+                  print_import_ok (pk, NULL, 0);
+       
+           if( !opt.quiet ) {
+               char *p=get_user_id_printable(keyid);
+               log_info( _("key %08lX: \"%s\" not changed\n"),
+                         (ulong)keyid[1],p);
+               m_free(p);
+           }
+           stats->unchanged++;
+       }
+        keydb_release (hd); hd = NULL;
     }
 
   leave:
     release_kbnode( keyblock_orig );
-    free_public_cert( pkc_orig );
+    free_public_key( pk_orig );
+
+    revocation_present(keyblock);
+
+    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=m_alloc_clear(sizeof(PACKET));
+         PKT_public_key *pk=m_alloc_clear(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.
+ * We allow secret key importing only when allow is true, this is so
+ * that a secret key can not be imported accidently and thereby tampering
+ * with the trust calculation.
+ */
+static int
+import_secret_one( const char *fname, KBNODE keyblock, 
+                   struct stats_s *stats, unsigned int options)
+{
+    PKT_secret_key *sk;
+    KBNODE node, uidnode;
+    u32 keyid[2];
+    int rc = 0;
+
+    /* get the key and print some info about it */
+    node = find_kbnode( keyblock, PKT_SECRET_KEY );
+    if( !node )
+       BUG();
+
+    sk = node->pkt->pkt.secret_key;
+    keyid_from_sk( sk, keyid );
+    uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
+
+    if( opt.verbose ) {
+       log_info( "sec  %4u%c/%08lX %s   ",
+                 nbits_from_sk( sk ),
+                 pubkey_letter( sk->pubkey_algo ),
+                 (ulong)keyid[1], datestr_from_sk(sk) );
+       if( uidnode )
+           print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+                                      uidnode->pkt->pkt.user_id->len );
+       putc('\n', stderr);
+    }
+    stats->secret_read++;
+
+    if( !uidnode ) {
+       log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]);
+       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.import_options&IMPORT_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));
+         keydb_release (hd);
+         return G10ERR_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_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));
+
     return rc;
 }
 
 
 /****************
- * Import a revocation certificate, this is a single signature packet.
+ * Import a revocation certificate; this is a single signature packet.
  */
 static int
-import_revoke_cert( const char *fname, KBNODE node )
+import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
 {
-    PKT_public_cert *pkc=NULL;
+    PKT_public_key *pk=NULL;
     KBNODE onode, keyblock = NULL;
-    KBPOS kbpos;
+    KEYDB_HANDLE hd = NULL;
     u32 keyid[2];
     int rc = 0;
 
@@ -395,32 +976,40 @@ import_revoke_cert( const char *fname, KBNODE node )
     keyid[0] = node->pkt->pkt.signature->keyid[0];
     keyid[1] = node->pkt->pkt.signature->keyid[1];
 
-    pkc = m_alloc_clear( sizeof *pkc );
-    rc = get_pubkey( pkc, keyid );
+    pk = m_alloc_clear( sizeof *pk );
+    rc = get_pubkey( pk, keyid );
     if( rc == G10ERR_NO_PUBKEY ) {
-       log_info("%s: key %08lX, no public key - "
-                "can't apply revocation certificate\n",
-                               fname, (ulong)keyid[1]);
+       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("%s: key %08lX, public key not found: %s\n",
-                               fname, (ulong)keyid[1], g10_errstr(rc));
+       log_error( _("key %08lX: public key not found: %s\n"),
+                                      (ulong)keyid[1], g10_errstr(rc));
        goto leave;
     }
 
     /* read the original keyblock */
-    rc = find_keyblock_bypkc( &kbpos, pkc );
-    if( rc ) {
-       log_error("%s: key %08lX, can't locate original keyblock: %s\n",
-                                fname, (ulong)keyid[1], g10_errstr(rc));
+    hd = keydb_new (0);
+    {
+        byte afp[MAX_FINGERPRINT_LEN];
+        size_t an;
+        
+        fingerprint_from_pk (pk, afp, &an);
+        while (an < MAX_FINGERPRINT_LEN) 
+            afp[an++] = 0;
+        rc = keydb_search_fpr (hd, afp);
+    }
+    if (rc) {
+       log_error (_("key %08lX: can't locate original keyblock: %s\n"),
+                   (ulong)keyid[1], g10_errstr(rc));
        goto leave;
     }
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("%s: key %08lX, can't read original keyblock: %s\n",
-                                fname, (ulong)keyid[1], g10_errstr(rc));
+    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));
        goto leave;
     }
 
@@ -430,23 +1019,23 @@ import_revoke_cert( const char *fname, KBNODE node )
      * special case. */
     rc = check_key_signature( keyblock, node, NULL);
     if( rc ) {
-       log_error("%s: key %08lX, invalid revocation certificate"
-                 ": %s - rejected\n",
-                 fname, (ulong)keyid[1], g10_errstr(rc));
+       log_error( _("key %08lX: invalid revocation certificate"
+                 ": %s - rejected\n"), (ulong)keyid[1], g10_errstr(rc));
+       goto leave;
     }
 
 
-    /* check wether we already have this */
+    /* check whether we already have this */
     for(onode=keyblock->next; onode; onode=onode->next ) {
        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] ) {
+                && !cmp_signatures(node->pkt->pkt.signature,
+                                   onode->pkt->pkt.signature))
+         {
            rc = 0;
            goto leave; /* yes, we already know about it */
-       }
+         }
     }
 
 
@@ -454,90 +1043,230 @@ import_revoke_cert( const char *fname, KBNODE node )
     insert_kbnode( keyblock, clone_kbnode(node), 0 );
 
     /* and write the keyblock back */
-    if( opt.verbose > 1 )
-       log_info("%s: writing to '%s'\n",
-                           fname, keyblock_resource_name(&kbpos) );
-    if( (rc=lock_keyblock( &kbpos )) )
-       log_error("can't lock public keyring '%s': %s\n",
-                        keyblock_resource_name(&kbpos), g10_errstr(rc) );
-    else if( (rc=update_keyblock( &kbpos, keyblock )) )
-       log_error("%s: can't write to '%s': %s\n", fname,
-                        keyblock_resource_name(&kbpos), g10_errstr(rc) );
-    unlock_keyblock( &kbpos );
+    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_release (hd); hd = NULL;
     /* we are ready */
-    log_info("%s: key %08lX, added revocation certificate\n",
-                                fname, (ulong)keyid[1]);
+    if( !opt.quiet ) {
+        char *p=get_user_id_printable (keyid);
+       log_info( _("key %08lX: \"%s\" revocation certificate imported\n"),
+                                       (ulong)keyid[1],p);
+       m_free(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:
+    keydb_release (hd);
     release_kbnode( keyblock );
-    free_public_cert( pkc );
+    free_public_key( pk );
     return rc;
 }
 
 
 /****************
- * loop over the keyblock an check all self signatures.
+ * 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.  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_cert *pkc, u32 *keyid )
+              PKT_public_key *pk, u32 *keyid, int *non_self )
 {
-    KBNODE n, unode;
+    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] ) {
-           unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
-           if( !unode )  {
-               log_error("%s: key %08lX, no user-id for signature\n",
-                                       fname, (ulong)keyid[1]);
-               return -1;  /* the complete keyblock is invalid */
+
+         /* 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 )  {
+                   log_error( _("key %08lX: no user ID for signature\n"),
+                                           (ulong)keyid[1]);
+                   return -1;  /* the complete keyblock is invalid */
+               }
+
+               /* If it hasn't been marked valid yet, keep trying */
+               if(!(unode->flag&1)) {
+                 rc = check_key_signature( keyblock, n, NULL);
+                 if( rc )
+                   {
+                     if( opt.verbose )
+                       {
+                         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
+                   unode->flag |= 1; /* mark that signature checked */
+               }
            }
-           rc = check_key_signature( keyblock, n, NULL);
-           if( rc ) {
-               log_error("%s: key %08lX, invalid self-signature\n",
-                                       fname, (ulong)keyid[1]);
-               unode->flag |= 2; /* mark as invalid */
+           else if( sig->sig_class == 0x18 ) {
+             /* 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 key binding\n"),
+                               (ulong)keyid[1]);
+                   n->flag |= 4; /* delete this */
+               }
+               else {
+                 rc = check_key_signature( keyblock, n, NULL);
+                 if( rc ) {
+                   if(opt.verbose)
+                     log_info(rc == G10ERR_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 ) {
+                   if(opt.verbose)
+                     log_info( _("key %08lX: no subkey for key revocation\n"),
+                               (ulong)keyid[1]);
+                   n->flag |= 4; /* delete this */
+               }
+               else {
+                 rc = check_key_signature( keyblock, n, NULL);
+                 if( rc ) {
+                     if(opt.verbose)
+                       log_info(rc == G10ERR_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\n"),(ulong)keyid[1]);
+                     }
+
+                     rsnode=n;
+                     rsdate=sig->timestamp;
+                   }
+                   else
+                     n->flag|=4; /* older */
+                 }
+               }
            }
-           unode->flag |= 1; /* mark that user-id checked */
        }
+       else
+         *non_self=1;
     }
+
     return 0;
 }
 
 /****************
- * delete all parts which are invalidand those signatures whos
- * public key algorithm is not availabe in this implemenation;
+ * delete all parts which are invalid and those signatures whose
+ * public key algorithm is not available in this implemenation;
  * but consider RSA as valid, because parse/build_packets knows
  * about it.
  * 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 ) {
            uid_seen = 1;
            if( (node->flag & 2) || !(node->flag & 1) ) {
                if( opt.verbose ) {
-                   log_info("%s: key %08lX, removed userid '",
-                                                 fname, (ulong)keyid[1]);
-                   print_string( stderr, node->pkt->pkt.user_id->name,
-                                     node->pkt->pkt.user_id->len, 0 );
+                   log_info( _("key %08lX: skipped user ID '"),
+                                                        (ulong)keyid[1]);
+                   print_utf8_string( stderr, node->pkt->pkt.user_id->name,
+                                      node->pkt->pkt.user_id->len );
                    fputs("'\n", stderr );
                }
                delete_kbnode( node ); /* the user-id */
                /* and all following packets up to the next user-id */
-               while( node->next && node->next->pkt->pkttype != PKT_USER_ID ){
+               while( node->next
+                      && node->next->pkt->pkttype != PKT_USER_ID
+                      && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
+                      && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ){
                    delete_kbnode( node->next );
                    node = node->next;
                }
@@ -545,27 +1274,91 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
            else
                nvalid++;
        }
+       else if(    node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+           if( (node->flag & 2) || !(node->flag & 1) ) {
+               if( opt.verbose ) {
+                   log_info( _("key %08lX: skipped subkey\n"),
+                                                        (ulong)keyid[1]);
+               }
+               delete_kbnode( node ); /* the subkey */
+               /* and all following signature packets */
+               while( node->next
+                      && node->next->pkt->pkttype == PKT_SIGNATURE ) {
+                   delete_kbnode( node->next );
+                   node = node->next;
+               }
+           }
+           else
+             subkey_seen = 1;
+       }
        else if( node->pkt->pkttype == PKT_SIGNATURE
                 && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo)
                 && 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
+            * to import non-exportable signature when we have the
+            * the secret key used to create this signature - it
+            * seems that this makes sense */
+           if(opt.verbose)
+             log_info( _("key %08lX: non exportable signature "
+                         "(class %02x) - skipped\n"),
+                       (ulong)keyid[1], node->pkt->pkt.signature->sig_class );
+           delete_kbnode( node );
+       }
        else if( node->pkt->pkttype == PKT_SIGNATURE
                 && node->pkt->pkt.signature->sig_class == 0x20 )  {
            if( uid_seen ) {
-               log_error("%s: key %08lX, revocation certificate at wrong "
-                          "place - removed\n", fname, (ulong)keyid[1]);
+               if(opt.verbose)
+                 log_info( _("key %08lX: revocation certificate "
+                             "at wrong place - skipped\n"), (ulong)keyid[1]);
                delete_kbnode( node );
            }
            else {
-               int rc = check_key_signature( keyblock, node, NULL);
-               if( rc ) {
-                   log_error("%s: key %08lX, invalid revocation certificate"
-                             ": %s - removed\n",
-                             fname, (ulong)keyid[1], g10_errstr(rc));
-                   delete_kbnode( node );
+             /* If the revocation cert is from a different key than
+                 the one we're working on don't check it - it's
+                 probably from a revocation key and won't be
+                 verifiable with this key anyway. */
+
+             if(node->pkt->pkt.signature->keyid[0]==keyid[0] &&
+                node->pkt->pkt.signature->keyid[1]==keyid[1])
+               {
+                 int rc = check_key_signature( keyblock, node, NULL);
+                 if( rc )
+                   {
+                     if(opt.verbose)
+                       log_info( _("key %08lX: invalid revocation "
+                                   "certificate: %s - skipped\n"),
+                                 (ulong)keyid[1], g10_errstr(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 );
     }
 
     /* note: because keyblock is the public key, it is never marked
@@ -576,21 +1369,187 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
 
 
 /****************
+ * It may happen that the imported keyblock has duplicated user IDs.
+ * We check this here and collapse those user IDs together with their
+ * sigs into one.
+ * Returns: True if the keyblock hash changed.
+ */
+int
+collapse_uids( KBNODE *keyblock )
+{
+    KBNODE n, n2;
+    int in_uid;
+    int any=0;
+    u32 kid1;
+
+  restart:
+    for( n = *keyblock; n; n = n->next ) {
+       if( n->pkt->pkttype != PKT_USER_ID )
+           continue;
+       for( n2 = n->next; n2; n2 = n2->next ) {
+           if( n2->pkt->pkttype == PKT_USER_ID
+               && !cmp_user_ids( n->pkt->pkt.user_id,
+                                 n2->pkt->pkt.user_id ) ) {
+               /* found a duplicate */
+               any = 1;
+               if( !n2->next
+                   || n2->next->pkt->pkttype == PKT_USER_ID
+                   || n2->next->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                   || n2->next->pkt->pkttype == PKT_SECRET_SUBKEY  ) {
+                   /* no more signatures: delete the user ID
+                    * and start over */
+                   remove_kbnode( keyblock, n2 );
+               }
+               else {
+                   /* The simple approach: Move one signature and
+                    * then start over to delete the next one :-( */
+                   move_kbnode( keyblock, n2->next, n->next );
+               }
+               goto restart;
+           }
+       }
+    }
+    if( !any )
+       return 0;
+
+  restart_sig:
+    /* now we may have duplicate signatures on one user ID: fix this */
+    for( in_uid = 0, n = *keyblock; n; n = n->next ) {
+       if( n->pkt->pkttype == PKT_USER_ID )
+           in_uid = 1;
+       else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                || n->pkt->pkttype == PKT_SECRET_SUBKEY )
+           in_uid = 0;
+       else if( in_uid ) {
+           n2 = n;
+           do {
+               KBNODE ncmp = NULL;
+               for( ; n2; n2 = n2->next ) {
+                   if(    n2->pkt->pkttype == PKT_USER_ID
+                       || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                       || n2->pkt->pkttype == PKT_SECRET_SUBKEY )
+                       break;
+                   if( n2->pkt->pkttype != PKT_SIGNATURE )
+                       ;
+                   else if( !ncmp )
+                       ncmp = n2;
+                   else if( !cmp_signatures( ncmp->pkt->pkt.signature,
+                                               n2->pkt->pkt.signature )) {
+                       remove_kbnode( keyblock, n2 );
+                       goto restart_sig;
+                   }
+               }
+               n2 = ncmp? ncmp->next : NULL;
+           } while( n2 );
+       }
+    }
+
+    if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
+       kid1 = keyid_from_pk( n->pkt->pkt.public_key, NULL );
+    else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
+       kid1 = keyid_from_sk( n->pkt->pkt.secret_key, NULL );
+    else
+       kid1 = 0;
+    if(!opt.quiet)
+      log_info(_("key %08lX: duplicated user ID detected - merged\n"),
+              (ulong)kid1);
+
+    return 1;
+}
+
+/* Check for a 0x20 revocation from a revocation key that is not
+   present.  This gets called without the benefit of merge_xxxx so you
+   can't rely on pk->revkey and friends. */
+static void
+revocation_present(KBNODE keyblock)
+{
+  KBNODE onode,inode;
+  PKT_public_key *pk=keyblock->pkt->pkt.public_key;
+
+  for(onode=keyblock->next;onode;onode=onode->next)
+    {
+      /* If we reach user IDs, we're done. */
+      if(onode->pkt->pkttype==PKT_USER_ID)
+       break;
+
+      if(onode->pkt->pkttype==PKT_SIGNATURE &&
+        onode->pkt->pkt.signature->sig_class==0x1F &&
+        onode->pkt->pkt.signature->revkey)
+       {
+         int idx;
+         PKT_signature *sig=onode->pkt->pkt.signature;
+
+         for(idx=0;idx<sig->numrevkeys;idx++)
+           {
+             u32 keyid[2];
+
+             keyid_from_fingerprint(sig->revkey[idx]->fpr,
+                                    MAX_FINGERPRINT_LEN,keyid);
+
+             for(inode=keyblock->next;inode;inode=inode->next)
+               {
+                 /* If we reach user IDs, we're done. */
+                 if(inode->pkt->pkttype==PKT_USER_ID)
+                   break;
+
+                 if(inode->pkt->pkttype==PKT_SIGNATURE &&
+                    inode->pkt->pkt.signature->sig_class==0x20 &&
+                    inode->pkt->pkt.signature->keyid[0]==keyid[0] &&
+                    inode->pkt->pkt.signature->keyid[1]==keyid[1])
+                   {
+                     /* Okay, we have a revocation key, and a
+                         revocation issued by it.  Do we have the key
+                         itself? */
+                     int rc;
+
+                     rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr,
+                                                   MAX_FINGERPRINT_LEN);
+                     if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_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: "
+                                        "fetching revocation key %08lX\n"),
+                                      (ulong)keyid_from_pk(pk,NULL),
+                                      (ulong)keyid[1]);
+                             keyserver_import_fprint(sig->revkey[idx]->fpr,
+                                                     MAX_FINGERPRINT_LEN);
+
+                             /* Do we have it now? */
+                             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: "
+                                      "revocation key %08lX not present.\n"),
+                                    (ulong)keyid_from_pk(pk,NULL),
+                                    (ulong)keyid[1]);
+                       }
+                   }
+               }
+           }
+       }
+    }
+}
+
+/****************
  * compare and merge the blocks
  *
  * o compare the signatures: If we already have this signature, check
  *   that they compare okay; if not, issue a warning and ask the user.
- *   FIXME: add the check, that we don` have duplicate signatures and the
- *   warning in cases that the old/new signatures don't match.
  * o Simply add the signature. Can't verify here because we may not have
- *   the signatures public key yet; verification is done when putting it
+ *   the signature's public key yet; verification is done when putting it
  *   into the trustdb, which is done automagically as soon as this pubkey
  *   is used.
  * Note: We indicate newly inserted packets with flag bit 0
  */
 static int
 merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
-                                  u32 *keyid, int *n_uids, int *n_sigs )
+             u32 *keyid, int *n_uids, int *n_sigs, int *n_subk )
 {
     KBNODE onode, node;
     int rc, found;
@@ -601,17 +1560,51 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
            break;
        else if( node->pkt->pkttype == PKT_SIGNATURE
                 && node->pkt->pkt.signature->sig_class == 0x20 )  {
-           /* check wether we already have this */
+           /* check whether we already have this */
            found = 0;
            for(onode=keyblock_orig->next; onode; onode=onode->next ) {
                if( onode->pkt->pkttype == PKT_USER_ID )
                    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] ) {
+                        && !cmp_signatures(onode->pkt->pkt.signature,
+                                           node->pkt->pkt.signature))
+                 {
+                   found = 1;
+                   break;
+                 }
+           }
+           if( !found ) {
+               KBNODE n2 = clone_kbnode(node);
+               insert_kbnode( keyblock_orig, n2, 0 );
+               n2->flag |= 1;
+                ++*n_sigs;
+               if(!opt.quiet)
+                 {
+                   char *p=get_user_id_printable (keyid);
+                   log_info(_("key %08lX: \"%s\" revocation "
+                              "certificate added\n"), (ulong)keyid[1],p);
+                   m_free(p);
+                 }
+           }
+       }
+    }
+
+    /* 2nd: merge in any direct key (0x1F) sigs */
+    for(node=keyblock->next; node; node=node->next ) {
+       if( node->pkt->pkttype == PKT_USER_ID )
+           break;
+       else if( node->pkt->pkttype == PKT_SIGNATURE
+                && node->pkt->pkt.signature->sig_class == 0x1F )  {
+           /* check whether we already have this */
+           found = 0;
+           for(onode=keyblock_orig->next; onode; onode=onode->next ) {
+               if( onode->pkt->pkttype == PKT_USER_ID )
+                   break;
+               else if( onode->pkt->pkttype == PKT_SIGNATURE
+                        && onode->pkt->pkt.signature->sig_class == 0x1F
+                        && !cmp_signatures(onode->pkt->pkt.signature,
+                                           node->pkt->pkt.signature)) {
                    found = 1;
                    break;
                }
@@ -620,20 +1613,20 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                KBNODE n2 = clone_kbnode(node);
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
-               node->flag |= 1;
-               log_info("%s: key %08lX, added revocation certificate\n",
-                                        fname, (ulong)keyid[1]);
+                ++*n_sigs;
+               if(!opt.quiet)
+                 log_info( _("key %08lX: direct key signature added\n"),
+                           (ulong)keyid[1]);
            }
        }
     }
 
-    /* 2nd: try to merge new ones in */
+    /* 3rd: try to merge new certificates in */
     for(onode=keyblock_orig->next; onode; onode=onode->next ) {
        if( !(onode->flag & 1) && onode->pkt->pkttype == PKT_USER_ID) {
            /* find the user id in the imported keyblock */
            for(node=keyblock->next; node; node=node->next )
-               if( !(node->flag & 1)
-                   && node->pkt->pkttype == PKT_USER_ID
+               if( node->pkt->pkttype == PKT_USER_ID
                    && !cmp_user_ids( onode->pkt->pkt.user_id,
                                          node->pkt->pkt.user_id ) )
                    break;
@@ -645,17 +1638,16 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
        }
     }
 
-    /* 3rd: add new user-ids */
+    /* 4th: add new user-ids */
     for(node=keyblock->next; node; node=node->next ) {
-       if( !(node->flag & 1) && node->pkt->pkttype == PKT_USER_ID) {
+       if( node->pkt->pkttype == PKT_USER_ID) {
            /* do we have this in the original keyblock */
            for(onode=keyblock_orig->next; onode; onode=onode->next )
-               if( !(onode->flag & 1)
-                   && onode->pkt->pkttype == PKT_USER_ID
-                   && cmp_user_ids( onode->pkt->pkt.user_id,
-                                    node->pkt->pkt.user_id ) )
+               if( onode->pkt->pkttype == PKT_USER_ID
+                   && !cmp_user_ids( onode->pkt->pkt.user_id,
+                                     node->pkt->pkt.user_id ) )
                    break;
-           if( !node ) { /* this is a new user id: append */
+           if( !onode ) { /* this is a new user id: append */
                rc = append_uid( keyblock_orig, node, n_sigs, fname, keyid);
                if( rc )
                    return rc;
@@ -664,37 +1656,107 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
        }
     }
 
+    /* 5th: add new subkeys */
+    for(node=keyblock->next; node; node=node->next ) {
+       onode = NULL;
+       if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+           /* do we have this in the original keyblock? */
+           for(onode=keyblock_orig->next; onode; onode=onode->next )
+               if( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                   && !cmp_public_keys( onode->pkt->pkt.public_key,
+                                        node->pkt->pkt.public_key ) )
+                   break;
+           if( !onode ) { /* this is a new subkey: append */
+               rc = append_key( keyblock_orig, node, n_sigs, fname, keyid);
+               if( rc )
+                   return rc;
+               ++*n_subk;
+           }
+       }
+       else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+           /* do we have this in the original keyblock? */
+           for(onode=keyblock_orig->next; onode; onode=onode->next )
+               if( onode->pkt->pkttype == PKT_SECRET_SUBKEY
+                   && !cmp_secret_keys( onode->pkt->pkt.secret_key,
+                                        node->pkt->pkt.secret_key ) )
+                   break;
+           if( !onode ) { /* this is a new subkey: append */
+               rc = append_key( keyblock_orig, node, n_sigs, fname, keyid);
+               if( rc )
+                   return rc;
+               ++*n_subk;
+           }
+       }
+    }
+
+    /* 6th: merge subkey certificates */
+    for(onode=keyblock_orig->next; onode; onode=onode->next ) {
+       if( !(onode->flag & 1)
+           &&  (   onode->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                || onode->pkt->pkttype == PKT_SECRET_SUBKEY) ) {
+           /* find the subkey in the imported keyblock */
+           for(node=keyblock->next; node; node=node->next ) {
+               if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                   && !cmp_public_keys( onode->pkt->pkt.public_key,
+                                         node->pkt->pkt.public_key ) )
+                   break;
+               else if( node->pkt->pkttype == PKT_SECRET_SUBKEY
+                   && !cmp_secret_keys( onode->pkt->pkt.secret_key,
+                                         node->pkt->pkt.secret_key ) )
+                   break;
+           }
+           if( node ) { /* found: merge */
+               rc = merge_keysigs( onode, node, n_sigs, fname, keyid );
+               if( rc )
+                   return rc;
+           }
+       }
+    }
+
+
     return 0;
 }
 
 
 /****************
  * append the userid starting with NODE and all signatures to KEYBLOCK.
- * Mark all new and copied packets by setting flag bit 0.
  */
 static int
 append_uid( KBNODE keyblock, KBNODE node, int *n_sigs,
                                          const char *fname, u32 *keyid )
 {
-    KBNODE n;
+    KBNODE n, n_where=NULL;
 
     assert(node->pkt->pkttype == PKT_USER_ID );
-    /* at lease a self signature comes next to the user-id */
-    if( node->next->pkt->pkttype == PKT_USER_ID ) {
-       log_error("%s: key %08lX, our copy has no self-signature\n",
-                                                 fname, (ulong)keyid[1]);
-       return G10ERR_GENERAL;
+
+    /* find the position */
+    for( n = keyblock; n; n_where = n, n = n->next ) {
+       if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
+           || n->pkt->pkttype == PKT_SECRET_SUBKEY )
+           break;
     }
+    if( !n )
+       n_where = NULL;
 
-    for( ;node && node->pkt->pkttype != PKT_USER_ID; node = node->next ) {
+    /* and append/insert */
+    while( node ) {
        /* we add a clone to the original keyblock, because this
         * one is released first */
        n = clone_kbnode(node);
-       add_kbnode( keyblock, n );
-       node->flag |= 1;
+       if( n_where ) {
+           insert_kbnode( n_where, n, 0 );
+           n_where = n;
+       }
+       else
+           add_kbnode( keyblock, n );
        n->flag |= 1;
+       node->flag |= 1;
        if( n->pkt->pkttype == PKT_SIGNATURE )
            ++*n_sigs;
+
+       node = node->next;
+       if( node && node->pkt->pkttype != PKT_SIGNATURE )
+           break;
     }
 
     return 0;
@@ -714,48 +1776,114 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
 
     assert(dst->pkt->pkttype == PKT_USER_ID );
     assert(src->pkt->pkttype == PKT_USER_ID );
-    /* at least a self signature comes next to the user-ids */
-    assert(src->next->pkt->pkttype != PKT_USER_ID );
-    if( dst->next->pkt->pkttype == PKT_USER_ID ) {
-       log_error("%s: key %08lX, our copy has no self-signature\n",
-                                                 fname, (ulong)keyid[1]);
-       return 0;
-    }
-
 
     for(n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next ) {
        if( n->pkt->pkttype != PKT_SIGNATURE )
            continue;
+       if( n->pkt->pkt.signature->sig_class == 0x18
+           || 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(!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
+            * one is released first */
+           n2 = clone_kbnode(n);
+           insert_kbnode( dst, n2, PKT_SIGNATURE );
+           n2->flag |= 1;
+           n->flag |= 1;
+           ++*n_sigs;
+       }
+    }
+
+    return 0;
+}
+
+/****************
+ * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY.
+ */
+static int
+merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
+                                   const char *fname, u32 *keyid )
+{
+    KBNODE n, n2;
+    int found=0;
+
+    assert(   dst->pkt->pkttype == PKT_PUBLIC_SUBKEY
+          || dst->pkt->pkttype == PKT_SECRET_SUBKEY );
+
+    for(n=src->next; n ; n = n->next ) {
+       if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
+           || n->pkt->pkttype == PKT_PUBLIC_KEY )
+           break;
+       if( n->pkt->pkttype != PKT_SIGNATURE )
+           continue;
+       found = 0;
+       for(n2=dst->next; n2; n2 = n2->next){
+           if( n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+               || n2->pkt->pkttype == PKT_PUBLIC_KEY )
+               break;
            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] ) {
-           found++;
-           break;
+                  == 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;
+           }
        }
-
-       if( found ) { /* we already have this signature */
-           /* Hmmm: should we compare the timestamp etc?
-            * but then we have first to see wether this signature is valid
-            * - or - simply add it in such a case and let trustdb logic
-            * decide wether to remove the old one
-            */
-           continue;
+       if( !found ) {
+           /* This signature is new or newer, append N to DST.
+            * We add a clone to the original keyblock, because this
+            * one is released first */
+           n2 = clone_kbnode(n);
+           insert_kbnode( dst, n2, PKT_SIGNATURE );
+           n2->flag |= 1;
+           n->flag |= 1;
+           ++*n_sigs;
        }
+    }
 
-       /* This signature is new, append N to DST it.
-        * We add a clone to the original keyblock, because this
+    return 0;
+}
+
+/****************
+ * append the subkey starting with NODE and all signatures to KEYBLOCK.
+ * Mark all new and copied packets by setting flag bit 0.
+ */
+static int
+append_key( KBNODE keyblock, KBNODE node, int *n_sigs,
+                                         const char *fname, u32 *keyid )
+{
+    KBNODE n;
+
+    assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+          || node->pkt->pkttype == PKT_SECRET_SUBKEY );
+
+    while(  node ) {
+       /* we add a clone to the original keyblock, because this
         * one is released first */
-       n2 = clone_kbnode(n);
-       insert_kbnode( dst, n2, PKT_USER_ID );
-       n2->flag |= 1;
+       n = clone_kbnode(node);
+       add_kbnode( keyblock, n );
        n->flag |= 1;
-       ++*n_sigs;
+       node->flag |= 1;
+       if( n->pkt->pkttype == PKT_SIGNATURE )
+           ++*n_sigs;
+
+       node = node->next;
+       if( node && node->pkt->pkttype != PKT_SIGNATURE )
+           break;
     }
 
     return 0;
 }
-