speedo: Improve speedo Makefile.
[gnupg.git] / g10 / import.c
index a58637c..ca35ce1 100644 (file)
@@ -1,6 +1,7 @@
 /* import.c - import a key into our key storage.
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- *               2007 Free Software Foundation, Inc.
+ *               2007, 2010, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2014  Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -37,6 +38,8 @@
 #include "ttyio.h"
 #include "status.h"
 #include "keyserver-internal.h"
+#include "call-agent.h"
+#include "../common/membuf.h"
 
 struct stats_s {
     ulong count;
@@ -58,15 +61,21 @@ struct stats_s {
 };
 
 
-static int import( IOBUF inp, const char* fname,struct stats_s *stats,
-                  unsigned char **fpr,size_t *fpr_len,unsigned int options );
+static int import (ctrl_t ctrl,
+                   IOBUF inp, const char* fname, struct stats_s *stats,
+                  unsigned char **fpr, size_t *fpr_len, unsigned int options,
+                  import_screener_t screener, void *screener_arg);
 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 char **fpr,size_t *fpr_len,
-                     unsigned int options,int from_sk);
-static int import_secret_one( const char *fname, KBNODE keyblock,
-                              struct stats_s *stats, unsigned int options);
+static void revocation_present (ctrl_t ctrl, kbnode_t keyblock);
+static int import_one (ctrl_t ctrl,
+                       const char *fname, KBNODE keyblock,struct stats_s *stats,
+                       unsigned char **fpr, size_t *fpr_len,
+                       unsigned int options, int from_sk, int silent,
+                       import_screener_t screener, void *screener_arg);
+static int import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock,
+                              struct stats_s *stats, int batch,
+                              unsigned int options, int for_migration,
+                              import_screener_t screener, void *screener_arg);
 static int import_revoke_cert( const char *fname, KBNODE node,
                                struct stats_s *stats);
 static int chk_self_sigs( const char *fname, KBNODE keyblock,
@@ -96,8 +105,6 @@ parse_import_options(char *str,unsigned int *options,int noisy)
        N_("repair damage from the pks keyserver during import")},
       {"fast-import",IMPORT_FAST,NULL,
        N_("do not update the trustdb after import")},
-      {"convert-sk-to-pk",IMPORT_SK2PK,NULL,
-       N_("create a public key when importing a secret key")},
       {"merge-only",IMPORT_MERGE_ONLY,NULL,
        N_("only accept updates to existing keys")},
       {"import-clean",IMPORT_CLEAN,NULL,
@@ -111,6 +118,8 @@ parse_import_options(char *str,unsigned int *options,int noisy)
       {"import-unusable-sigs",0,NULL,NULL},
       {"import-clean-sigs",0,NULL,NULL},
       {"import-clean-uids",0,NULL,NULL},
+      {"convert-sk-to-pk",0, NULL,NULL}, /* Not anymore needed due to
+                                            the new design.  */
       {NULL,0,NULL,NULL}
     };
 
@@ -161,9 +170,10 @@ import_release_stats_handle (void *p)
  *
  */
 static int
-import_keys_internal( IOBUF inp, char **fnames, int nnames,
+import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames,
                      void *stats_handle, unsigned char **fpr, size_t *fpr_len,
-                     unsigned int options )
+                     unsigned int options,
+                      import_screener_t screener, void *screener_arg)
 {
     int i, rc = 0;
     struct stats_s *stats = stats_handle;
@@ -172,7 +182,8 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
         stats = import_new_stats_handle ();
 
     if (inp) {
-        rc = import( inp, "[stream]", stats, fpr, fpr_len, options);
+      rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options,
+                   screener, screener_arg);
     }
     else {
         if( !fnames && !nnames )
@@ -187,18 +198,20 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
               {
                 iobuf_close (inp2);
                 inp2 = NULL;
-                errno = EPERM;
+                gpg_err_set_errno (EPERM);
               }
            if( !inp2 )
-               log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+               log_error(_("can't open '%s': %s\n"), fname, strerror(errno) );
            else
              {
-               rc = import( inp2, fname, stats, fpr, fpr_len, options );
+               rc = import (ctrl, inp2, fname, stats, fpr, fpr_len, options,
+                             screener, screener_arg);
                iobuf_close(inp2);
                 /* Must invalidate that ugly cache to actually close it. */
-                iobuf_ioctl (NULL, 2, 0, (char*)fname);
+                iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE,
+                             0, (char*)fname);
                if( rc )
-                 log_error("import from `%s' failed: %s\n", fname,
+                 log_error("import from '%s' failed: %s\n", fname,
                            g10_errstr(rc) );
              }
            if( !fname )
@@ -217,28 +230,60 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
        interactive or by not setting no-auto-check-trustdb */
 
     if(!(options&IMPORT_FAST))
-      trustdb_check_or_update();
+      check_or_update_trustdb ();
 
     return rc;
 }
 
+
 void
-import_keys( char **fnames, int nnames,
+import_keys (ctrl_t ctrl, char **fnames, int nnames,
             void *stats_handle, unsigned int options )
 {
-  import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options);
+  import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle,
+                        NULL, NULL, options, NULL, NULL);
 }
 
 int
-import_keys_stream( IOBUF inp, void *stats_handle,
-                   unsigned char **fpr, size_t *fpr_len,unsigned int options )
+import_keys_stream (ctrl_t ctrl, IOBUF inp, void *stats_handle,
+                   unsigned char **fpr, size_t *fpr_len, unsigned int options)
 {
-  return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options);
+  return import_keys_internal (ctrl, inp, NULL, 0, stats_handle,
+                               fpr, fpr_len, options, NULL, NULL);
 }
 
+
+/* Variant of import_keys_stream reading from an estream_t.  */
+int
+import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle,
+                       unsigned char **fpr, size_t *fpr_len,
+                       unsigned int options,
+                       import_screener_t screener, void *screener_arg)
+{
+  int rc;
+  iobuf_t inp;
+
+  inp = iobuf_esopen (fp, "r", 1);
+  if (!inp)
+    {
+      rc = gpg_error_from_syserror ();
+      log_error ("iobuf_esopen failed: %s\n", gpg_strerror (rc));
+      return rc;
+    }
+
+  rc = import_keys_internal (ctrl, inp, NULL, 0, stats_handle,
+                             fpr, fpr_len, options,
+                             screener, screener_arg);
+
+  iobuf_close (inp);
+  return rc;
+}
+
+
 static int
-import( IOBUF inp, const char* fname,struct stats_s *stats,
-       unsigned char **fpr,size_t *fpr_len,unsigned int options )
+import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats,
+       unsigned char **fpr,size_t *fpr_len, unsigned int options,
+       import_screener_t screener, void *screener_arg)
 {
     PACKET *pending_pkt = NULL;
     KBNODE keyblock = NULL;  /* Need to initialize because gcc can't
@@ -259,9 +304,13 @@ import( IOBUF inp, const char* fname,struct stats_s *stats,
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, stats, fpr, fpr_len, options, 0);
-       else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
-                rc = import_secret_one( fname, keyblock, stats, options );
+          rc = import_one (ctrl, fname, keyblock,
+                           stats, fpr, fpr_len, options, 0, 0,
+                           screener, screener_arg);
+       else if( keyblock->pkt->pkttype == PKT_SECRET_KEY )
+          rc = import_secret_one (ctrl, fname, keyblock, stats,
+                                  opt.batch, options, 0,
+                                  screener, screener_arg);
        else if( keyblock->pkt->pkttype == PKT_SIGNATURE
                 && keyblock->pkt->pkt.signature->sig_class == 0x20 )
            rc = import_revoke_cert( fname, keyblock, stats );
@@ -280,12 +329,64 @@ import( IOBUF inp, const char* fname,struct stats_s *stats,
     if( rc == -1 )
        rc = 0;
     else if( rc && rc != G10ERR_INV_KEYRING )
-       log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc));
+       log_error( _("error reading '%s': %s\n"), fname, g10_errstr(rc));
 
     return rc;
 }
 
 
+/* Helper to migrate secring.gpg to GnuPG 2.1.  */
+gpg_error_t
+import_old_secring (ctrl_t ctrl, const char *fname)
+{
+  gpg_error_t err;
+  iobuf_t inp;
+  PACKET *pending_pkt = NULL;
+  kbnode_t keyblock = NULL;  /* Need to initialize because gcc can't
+                                grasp the return semantics of
+                                read_block. */
+  struct stats_s *stats;
+
+  inp = iobuf_open (fname);
+  if (inp && is_secured_file (iobuf_get_fd (inp)))
+    {
+      iobuf_close (inp);
+      inp = NULL;
+      gpg_err_set_errno (EPERM);
+    }
+  if (!inp)
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err));
+      return err;
+    }
+
+  getkey_disable_caches();
+  stats = import_new_stats_handle ();
+  while (!(err = read_block (inp, &pending_pkt, &keyblock)))
+    {
+      if (keyblock->pkt->pkttype == PKT_SECRET_KEY)
+        err = import_secret_one (ctrl, fname, keyblock, stats, 1, 0, 1,
+                                 NULL, NULL);
+      release_kbnode (keyblock);
+      if (err)
+        break;
+    }
+  import_release_stats_handle (stats);
+  if (err == -1)
+    err = 0;
+  else if (err && gpg_err_code (err) != G10ERR_INV_KEYRING)
+    log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err));
+  else if (err)
+    log_error ("import from '%s' failed: %s\n", fname, gpg_strerror (err));
+
+  iobuf_close (inp);
+  iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname);
+
+  return err;
+}
+
+
 void
 import_print_stats (void *hd)
 {
@@ -350,11 +451,32 @@ import_print_stats (void *hd)
 }
 
 
+/* Return true if PKTTYPE is valid in a keyblock.  */
+static int
+valid_keyblock_packet (int pkttype)
+{
+  switch (pkttype)
+    {
+    case PKT_PUBLIC_KEY:
+    case PKT_PUBLIC_SUBKEY:
+    case PKT_SECRET_KEY:
+    case PKT_SECRET_SUBKEY:
+    case PKT_SIGNATURE:
+    case PKT_USER_ID:
+    case PKT_ATTRIBUTE:
+    case PKT_RING_TRUST:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+
 /****************
  * 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.
+ * Return: 0 = okay, -1 no more blocks or another errorcode.
  */
 static int
 read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
@@ -427,7 +549,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
            }
            in_cert = 1;
          default:
-           if( in_cert ) {
+           if (in_cert && valid_keyblock_packet (pkt->pkttype)) {
                if( !root )
                    root = new_kbnode( pkt );
                else
@@ -520,20 +642,57 @@ fix_pks_corruption(KBNODE keyblock)
 }
 
 
+/* Versions of GnuPG before 1.4.11 and 2.0.16 allowed to import bogus
+   direct key signatures.  A side effect of this was that a later
+   import of the same good direct key signatures was not possible
+   because the cmp_signature check in merge_blocks considered them
+   equal.  Although direct key signatures are now checked during
+   import, there might still be bogus signatures sitting in a keyring.
+   We need to detect and delete them before doing a merge.  This
+   function returns the number of removed sigs.  */
+static int
+fix_bad_direct_key_sigs (kbnode_t keyblock, u32 *keyid)
+{
+  gpg_error_t err;
+  kbnode_t node;
+  int count = 0;
+
+  for (node = keyblock->next; node; node=node->next)
+    {
+      if (node->pkt->pkttype == PKT_USER_ID)
+        break;
+      if (node->pkt->pkttype == PKT_SIGNATURE
+          && IS_KEY_SIG (node->pkt->pkt.signature))
+        {
+          err = check_key_signature (keyblock, node, NULL);
+          if (err && gpg_err_code (err) != GPG_ERR_PUBKEY_ALGO )
+            {
+              /* If we don't know the error, we can't decide; this is
+                 not a problem because cmp_signature can't compare the
+                 signature either.  */
+              log_info ("key %s: invalid direct key signature removed\n",
+                        keystr (keyid));
+              delete_kbnode (node);
+              count++;
+            }
+        }
+    }
+
+  return count;
+}
+
+
 static void
-print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
+print_import_ok (PKT_public_key *pk, 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);
+  snprintf (buf, sizeof buf, "%u ", reason);
   p = buf + strlen (buf);
 
-  if (pk)
-    fingerprint_from_pk (pk, array, &n);
-  else
-    fingerprint_from_sk (sk, array, &n);
+  fingerprint_from_pk (pk, array, &n);
   s = array;
   for (i=0; i < n ; i++, s++, p += 2)
     sprintf (p, "%02X", *s);
@@ -571,12 +730,12 @@ check_prefs_warning(PKT_public_key *pk)
 }
 
 static void
-check_prefs(KBNODE keyblock)
+check_prefs (ctrl_t ctrl, kbnode_t keyblock)
 {
-  KBNODE node;
+  kbnode_t node;
   PKT_public_key *pk;
   int problem=0;
-  
+
   merge_keys_and_selfsig(keyblock);
   pk=keyblock->pkt->pkt.public_key;
 
@@ -601,10 +760,10 @@ check_prefs(KBNODE keyblock)
                {
                  if (openpgp_cipher_test_algo (prefs->value))
                    {
-                     const char *algo = 
-                        (gcry_cipher_test_algo (prefs->value)
-                         ? num 
-                         : gcry_cipher_algo_name (prefs->value));
+                     const char *algo =
+                        (openpgp_cipher_test_algo (prefs->value)
+                         ? num
+                         : openpgp_cipher_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
                      log_info(_("         \"%s\": preference for cipher"
@@ -618,7 +777,7 @@ check_prefs(KBNODE keyblock)
                    {
                      const char *algo =
                         (gcry_md_test_algo (prefs->value)
-                         ? num 
+                         ? num
                          : gcry_md_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
@@ -668,7 +827,7 @@ check_prefs(KBNODE keyblock)
          append_to_strlist(&sl,"updpref");
          append_to_strlist(&sl,"save");
 
-         keyedit_menu( username, locusr, sl, 1, 1 );
+         keyedit_menu (ctrl, username, locusr, sl, 1, 1 );
          free_strlist(sl);
          free_strlist(locusr);
        }
@@ -679,15 +838,18 @@ check_prefs(KBNODE keyblock)
 }
 
 /****************
- * Try to import one keyblock. Return an error only in serious cases, but
- * never for an invalid keyblock.  It uses log_error to increase the
- * internal errorcount, so that invalid input can be detected by programs
- * which called g10.
+ * Try to import one keyblock. Return an error only in serious cases,
+ * but never for an invalid keyblock.  It uses log_error to increase
+ * the internal errorcount, so that invalid input can be detected by
+ * programs which called gpg.  If SILENT is no messages are printed -
+ * even most error messages are suppressed.
  */
 static int
-import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
-           unsigned char **fpr,size_t *fpr_len,unsigned int options,
-           int from_sk )
+import_one (ctrl_t ctrl,
+            const char *fname, KBNODE keyblock, struct stats_s *stats,
+           unsigned char **fpr, size_t *fpr_len, unsigned int options,
+           int from_sk, int silent,
+            import_screener_t screener, void *screener_arg)
 {
     PKT_public_key *pk;
     PKT_public_key *pk_orig;
@@ -697,7 +859,9 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     int rc = 0;
     int new_key = 0;
     int mod_key = 0;
+    int same_key = 0;
     int non_self = 0;
+    char pkstrbuf[PUBKEY_STRING_SIZE];
 
     /* get the key and print some info about it */
     node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
@@ -709,25 +873,34 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if( opt.verbose && !opt.interactive )
+    if (opt.verbose && !opt.interactive && !silent)
       {
-       log_info( "pub  %4u%c/%s %s  ",
-                 nbits_from_pk( pk ),
-                 pubkey_letter( pk->pubkey_algo ),
+       log_info( "pub  %s/%s %s  ",
+                 pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
                  keystr_from_pk(pk), datestr_from_pk(pk) );
-       if( uidnode )
-         print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+       if (uidnode)
+         print_utf8_buffer (log_get_stream (),
+                             uidnode->pkt->pkt.user_id->name,
                             uidnode->pkt->pkt.user_id->len );
        log_printf ("\n");
       }
 
+
     if( !uidnode )
       {
-       log_error( _("key %s: no user ID\n"), keystr_from_pk(pk));
+        if (!silent)
+          log_error( _("key %s: no user ID\n"), keystr_from_pk(pk));
        return 0;
       }
-    
-    if (opt.interactive) {
+
+    if (screener && screener (keyblock, screener_arg))
+      {
+        log_error (_("key %s: %s\n"), keystr_from_pk (pk),
+                   _("rejected by import screener"));
+        return 0;
+      }
+
+    if (opt.interactive && !silent) {
         if(is_status_enabled())
          print_import_check (pk, uidnode->pkt->pkt.user_id);
        merge_keys_and_selfsig (keyblock);
@@ -760,7 +933,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
        return rc== -1? 0:rc;
 
     /* If we allow such a thing, mark unsigned uids as valid */
-    if( opt.allow_non_selfsigned_uid )
+    if( opt.allow_non_selfsigned_uid)
       for( node=keyblock; node; node = node->next )
        if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) )
          {
@@ -773,9 +946,11 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
          }
 
     if( !delete_inv_parts( fname, keyblock, keyid, options ) ) {
-        log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk));
-       if( !opt.quiet )
-         log_info(_("this may be caused by a missing self-signature\n"));
+        if (!silent) {
+           log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk));
+           if( !opt.quiet )
+             log_info(_("this may be caused by a missing self-signature\n"));
+        }
        stats->no_user_id++;
        return 0;
     }
@@ -785,31 +960,32 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     rc = get_pubkey_fast ( pk_orig, keyid );
     if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY )
       {
-       log_error( _("key %s: public key not found: %s\n"),
-                  keystr(keyid), g10_errstr(rc));
+        if (!silent)
+          log_error (_("key %s: public key not found: %s\n"),
+                     keystr(keyid), g10_errstr(rc));
       }
     else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) )
       {
-       if( opt.verbose )
+       if( opt.verbose && !silent )
          log_info( _("key %s: new key - skipped\n"), keystr(keyid));
        rc = 0;
        stats->skipped_new_keys++;
       }
     else if( rc ) { /* insert this key */
-        KEYDB_HANDLE hd = keydb_new (0);
+        KEYDB_HANDLE hd = keydb_new ();
 
         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"), g10_errstr (rc));
             keydb_release (hd);
            return G10ERR_GENERAL;
        }
        if( opt.verbose > 1 )
-           log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
+           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"),
+          log_error (_("error writing keyring '%s': %s\n"),
                       keydb_get_resource_name (hd), g10_errstr(rc));
        else
          {
@@ -825,7 +1001,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
         keydb_release (hd);
 
        /* we are ready */
-       if( !opt.quiet )
+       if( !opt.quiet && !silent)
          {
            char *p=get_user_id_native (keyid);
            log_info( _("key %s: public key \"%s\" imported\n"),
@@ -837,7 +1013,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
            char *us = get_long_user_id_string( keyid );
            write_status_text( STATUS_IMPORTED, us );
            xfree(us);
-            print_import_ok (pk,NULL, 1);
+            print_import_ok (pk, 1);
          }
        stats->imported++;
        if( is_RSA( pk->pubkey_algo ) )
@@ -852,18 +1028,19 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
         * weird is going on */
        if( cmp_public_keys( pk_orig, pk ) )
          {
-           log_error( _("key %s: doesn't match our copy\n"),keystr(keyid));
+            if (!silent)
+              log_error( _("key %s: doesn't match our copy\n"),keystr(keyid));
            goto leave;
          }
 
        /* now read the original keyblock */
-        hd = keydb_new (0);
+        hd = keydb_new ();
         {
             byte afp[MAX_FINGERPRINT_LEN];
             size_t an;
 
             fingerprint_from_pk (pk_orig, afp, &an);
-            while (an < MAX_FINGERPRINT_LEN) 
+            while (an < MAX_FINGERPRINT_LEN)
                 afp[an++] = 0;
             rc = keydb_search_fpr (hd, afp);
         }
@@ -883,10 +1060,15 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
            goto leave;
          }
 
+        /* Make sure the original direct key sigs are all sane.  */
+        n_sigs_cleaned = fix_bad_direct_key_sigs (keyblock_orig, keyid);
+        if (n_sigs_cleaned)
+          commit_kbnode (&keyblock_orig);
+
        /* and try to merge the block */
        clear_kbnode_flags( keyblock_orig );
        clear_kbnode_flags( keyblock );
-       n_uids = n_sigs = n_subk = n_sigs_cleaned = n_uids_cleaned = 0;
+       n_uids = n_sigs = n_subk = n_uids_cleaned = 0;
        rc = merge_blocks( fname, keyblock_orig, keyblock,
                           keyid, &n_uids, &n_sigs, &n_subk );
        if( rc )
@@ -904,13 +1086,13 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
            /* keyblock_orig has been updated; write */
            rc = keydb_update_keyblock (hd, keyblock_orig);
             if (rc)
-               log_error (_("error writing keyring `%s': %s\n"),
+               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( !opt.quiet )
+           if( !opt.quiet && !silent)
              {
                char *p=get_user_id_native(keyid);
                if( n_uids == 1 )
@@ -952,16 +1134,16 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
            stats->n_sigs_cleaned +=n_sigs_cleaned;
            stats->n_uids_cleaned +=n_uids_cleaned;
 
-            if (is_status_enabled ()) 
-                 print_import_ok (pk, NULL,
-                                  ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
+            if (is_status_enabled () && !silent)
+              print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
        }
        else
          {
-           if (is_status_enabled ()) 
-             print_import_ok (pk, NULL, 0);
+            same_key = 1;
+            if (is_status_enabled ())
+             print_import_ok (pk, 0);
 
-           if( !opt.quiet )
+           if( !opt.quiet && !silent)
              {
                char *p=get_user_id_native(keyid);
                log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p);
@@ -975,18 +1157,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     }
 
   leave:
-
-    /* Now that the key is definitely incorporated into the keydb, we
-       need to check if a designated revocation is present or if the
-       prefs are not rational so we can warn the user. */
-
-    if(mod_key)
-      {
-       revocation_present(keyblock_orig);
-       if(!from_sk && seckey_available(keyid)==0)
-         check_prefs(keyblock_orig);
-      }
-    else if(new_key)
+    if (mod_key || new_key || same_key)
       {
        /* A little explanation for this: we fill in the fingerprint
           when importing keys as it can be useful to know the
@@ -994,21 +1165,41 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
           asked for a particular name, but the key doesn't have that
           name).  However, in cases where we're importing more than
           one key at a time, we cannot know which key to fingerprint.
-          In these cases, rather than guessing, we do not fingerpring
-          at all, and we must hope the user ID on the keys are
-          useful. */
-       if(fpr)
+          In these cases, rather than guessing, we do not
+          fingerprinting at all, and we must hope the user ID on the
+          keys are useful.  Note that we need to do this for new
+          keys, merged keys and even for unchanged keys.  This is
+          required because for example the --auto-key-locate feature
+          may import an already imported key and needs to know the
+          fingerprint of the key in all cases.  */
+       if (fpr)
          {
-           xfree(*fpr);
-           if(stats->imported==1)
-             *fpr=fingerprint_from_pk(pk,NULL,fpr_len);
+           xfree (*fpr);
+            /* Note that we need to compare against 0 here because
+               COUNT gets only incremented after returning form this
+               function.  */
+           if (stats->count == 0)
+             *fpr = fingerprint_from_pk (pk, NULL, fpr_len);
            else
-             *fpr=NULL;
+             *fpr = NULL;
          }
+      }
 
-       revocation_present(keyblock);
-       if(!from_sk && seckey_available(keyid)==0)
-         check_prefs(keyblock);
+    /* Now that the key is definitely incorporated into the keydb, we
+       need to check if a designated revocation is present or if the
+       prefs are not rational so we can warn the user. */
+
+    if (mod_key)
+      {
+       revocation_present (ctrl, keyblock_orig);
+       if (!from_sk && have_secret_key_with_kid (keyid))
+         check_prefs (ctrl, keyblock_orig);
+      }
+    else if (new_key)
+      {
+       revocation_present (ctrl, keyblock);
+       if (!from_sk && have_secret_key_with_kid (keyid))
+         check_prefs (ctrl, keyblock);
       }
 
     release_kbnode( keyblock_orig );
@@ -1017,66 +1208,323 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     return rc;
 }
 
-/* Walk a secret keyblock and produce a public keyblock out of it. */
-static KBNODE
-sec_to_pub_keyblock(KBNODE sec_keyblock)
+
+/* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent.  The
+   function prints diagnostics and returns an error code.  If BATCH is
+   true the secret keys are stored by gpg-agent in the transfer format
+   (i.e. no re-protection and aksing for passphrases). */
+static gpg_error_t
+transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock,
+                      int batch)
 {
-  KBNODE secnode,pub_keyblock=NULL,ctx=NULL;
+  gpg_error_t err = 0;
+  void *kek = NULL;
+  size_t keklen;
+  kbnode_t ctx = NULL;
+  kbnode_t node;
+  PKT_public_key *main_pk, *pk;
+  struct seckey_info *ski;
+  int nskey;
+  membuf_t mbuf;
+  int i, j;
+  void *format_args[2*PUBKEY_MAX_NSKEY];
+  gcry_sexp_t skey, prot, tmpsexp;
+  gcry_sexp_t curve = NULL;
+  unsigned char *transferkey = NULL;
+  size_t transferkeylen;
+  gcry_cipher_hd_t cipherhd = NULL;
+  unsigned char *wrappedkey = NULL;
+  size_t wrappedkeylen;
+  char *cache_nonce = NULL;
+
+  /* Get the current KEK.  */
+  err = agent_keywrap_key (ctrl, 0, &kek, &keklen);
+  if (err)
+    {
+      log_error ("error getting the KEK: %s\n", gpg_strerror (err));
+      goto leave;
+    }
 
-  while((secnode=walk_kbnode(sec_keyblock,&ctx,0)))
+  /* Prepare a cipher context.  */
+  err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
+                          GCRY_CIPHER_MODE_AESWRAP, 0);
+  if (!err)
+    err = gcry_cipher_setkey (cipherhd, kek, keklen);
+  if (err)
+    goto leave;
+  xfree (kek);
+  kek = NULL;
+
+  main_pk = NULL;
+  while ((node = walk_kbnode (sec_keyblock, &ctx, 0)))
     {
-      KBNODE pubnode;
+      if (node->pkt->pkttype != PKT_SECRET_KEY
+          && node->pkt->pkttype != PKT_SECRET_SUBKEY)
+        continue;
+      pk = node->pkt->pkt.public_key;
+      if (!main_pk)
+        main_pk = pk;
 
-      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. */
+      /* Make sure the keyids are available.  */
+      keyid_from_pk (pk, NULL);
+      if (node->pkt->pkttype == PKT_SECRET_KEY)
+        {
+          pk->main_keyid[0] = pk->keyid[0];
+          pk->main_keyid[1] = pk->keyid[1];
+        }
+      else
+        {
+          pk->main_keyid[0] = main_pk->keyid[0];
+          pk->main_keyid[1] = main_pk->keyid[1];
+        }
 
-         PKT_secret_key *sk=secnode->pkt->pkt.secret_key;
-         PACKET *pkt=xmalloc_clear(sizeof(PACKET));
-         PKT_public_key *pk=xmalloc_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;
+      ski = pk->seckey_info;
+      if (!ski)
+        BUG ();
 
-         pkt->pkt.public_key=pk;
+      stats->count++;
+      stats->secret_read++;
 
-         pk->version=sk->version;
-         pk->timestamp=sk->timestamp;
-         pk->expiredate=sk->expiredate;
-         pk->pubkey_algo=sk->pubkey_algo;
+      /* We ignore stub keys.  The way we handle them in other parts
+         of the code is by asking the agent whether any secret key is
+         available for a given keyblock and then concluding that we
+         have a secret key; all secret (sub)keys of the keyblock the
+         agent does not know of are then stub keys.  This works also
+         for card stub keys.  The learn command or the card-status
+         command may be used to check with the agent whether a card
+         has been inserted and a stub key is in turn generated by the
+         agent.  */
+      if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002)
+        continue;
 
-         n=pubkey_get_npkey(pk->pubkey_algo);
-         if(n==0)
-           {
-             /* we can't properly extract the pubkey without knowing
-                the number of MPIs */
-             release_kbnode(pub_keyblock);
-             return NULL;
-           }
-         else
-           {
-             int i;
+      /* Convert our internal secret key object into an S-expression.  */
+      nskey = pubkey_get_nskey (pk->pubkey_algo);
+      if (!nskey || nskey > PUBKEY_MAX_NSKEY)
+        {
+          err = gpg_error (GPG_ERR_BAD_SECKEY);
+          log_error ("internal error: %s\n", gpg_strerror (err));
+          goto leave;
+        }
 
-             for(i=0;i<n;i++)
-               pk->pkey[i]=mpi_copy(sk->skey[i]);
-           }
+      init_membuf (&mbuf, 50);
+      put_membuf_str (&mbuf, "(skey");
+      if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+          || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
+          || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+        {
+          /* The ECC case.  */
+          char *curvestr = openpgp_oid_to_str (pk->pkey[0]);
+          if (!curvestr)
+            err = gpg_error_from_syserror ();
+          else
+            {
+              err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvestr);
+              xfree (curvestr);
+              if (!err)
+                {
+                  j = 0;
+                  /* Append the public key element Q.  */
+                  put_membuf_str (&mbuf, " _ %m");
+                  format_args[j++] = pk->pkey + 1;
+
+                  /* Append the secret key element D.  For ECDH we
+                     skip PKEY[2] because this holds the KEK which is
+                     not needed by gpg-agent.  */
+                  i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2;
+                  if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
+                    put_membuf_str (&mbuf, " e %m");
+                  else
+                    put_membuf_str (&mbuf, " _ %m");
+                  format_args[j++] = pk->pkey + i;
+                }
+            }
+        }
+      else
+        {
+          /* Standard case for the old (non-ECC) algorithms.  */
+          for (i=j=0; i < nskey; i++)
+            {
+              if (!pk->pkey[i])
+                continue; /* Protected keys only have NPKEY+1 elements.  */
+
+              if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1))
+                put_membuf_str (&mbuf, " e %m");
+              else
+                put_membuf_str (&mbuf, " _ %m");
+              format_args[j++] = pk->pkey + i;
+            }
+        }
+      put_membuf_str (&mbuf, ")");
+      put_membuf (&mbuf, "", 1);
+      if (err)
+        xfree (get_membuf (&mbuf, NULL));
+      else
+        {
+          char *format = get_membuf (&mbuf, NULL);
+          if (!format)
+            err = gpg_error_from_syserror ();
+          else
+            err = gcry_sexp_build_array (&skey, NULL, format, format_args);
+          xfree (format);
+        }
+      if (err)
+        {
+          log_error ("error building skey array: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+
+      if (ski->is_protected)
+        {
+          char countbuf[35];
+
+          /* Note that the IVLEN may be zero if we are working on a
+             dummy key.  We can't express that in an S-expression and
+             thus we send dummy data for the IV.  */
+          snprintf (countbuf, sizeof countbuf, "%lu",
+                    (unsigned long)ski->s2k.count);
+          err = gcry_sexp_build
+            (&prot, NULL,
+             " (protection %s %s %b %d %s %b %s)\n",
+             ski->sha1chk? "sha1":"sum",
+             openpgp_cipher_algo_name (ski->algo),
+             ski->ivlen? (int)ski->ivlen:1,
+             ski->ivlen? ski->iv: (const unsigned char*)"X",
+             ski->s2k.mode,
+             openpgp_md_algo_name (ski->s2k.hash_algo),
+             (int)sizeof (ski->s2k.salt), ski->s2k.salt,
+             countbuf);
+        }
+      else
+        err = gcry_sexp_build (&prot, NULL, " (protection none)\n");
+
+      tmpsexp = NULL;
+      xfree (transferkey);
+      transferkey = NULL;
+      if (!err)
+        err = gcry_sexp_build (&tmpsexp, NULL,
+                               "(openpgp-private-key\n"
+                               " (version %d)\n"
+                               " (algo %s)\n"
+                               " %S%S\n"
+                               " (csum %d)\n"
+                               " %S)\n",
+                               pk->version,
+                               openpgp_pk_algo_name (pk->pubkey_algo),
+                               curve, skey,
+                               (int)(unsigned long)ski->csum, prot);
+      gcry_sexp_release (skey);
+      gcry_sexp_release (prot);
+      if (!err)
+        err = make_canon_sexp_pad (tmpsexp, 1, &transferkey, &transferkeylen);
+      gcry_sexp_release (tmpsexp);
+      if (err)
+        {
+          log_error ("error building transfer key: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+
+      /* Wrap the key.  */
+      wrappedkeylen = transferkeylen + 8;
+      xfree (wrappedkey);
+      wrappedkey = xtrymalloc (wrappedkeylen);
+      if (!wrappedkey)
+        err = gpg_error_from_syserror ();
+      else
+        err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen,
+                                   transferkey, transferkeylen);
+      if (err)
+        goto leave;
+      xfree (transferkey);
+      transferkey = NULL;
+
+      /* Send the wrapped key to the agent.  */
+      {
+        char *desc = gpg_format_keydesc (pk, FORMAT_KEYDESC_IMPORT, 1);
+        err = agent_import_key (ctrl, desc, &cache_nonce,
+                                wrappedkey, wrappedkeylen, batch);
+        xfree (desc);
+      }
+      if (!err)
+        {
+          if (opt.verbose)
+            log_info (_("key %s: secret key imported\n"),
+                      keystr_from_pk_with_sub (main_pk, pk));
+          stats->secret_imported++;
+        }
+      else if ( gpg_err_code (err) == GPG_ERR_EEXIST )
+        {
+          if (opt.verbose)
+            log_info (_("key %s: secret key already exists\n"),
+                      keystr_from_pk_with_sub (main_pk, pk));
+          err = 0;
+          stats->secret_dups++;
+        }
+      else
+        {
+          log_error (_("key %s: error sending to agent: %s\n"),
+                     keystr_from_pk_with_sub (main_pk, pk),
+                     gpg_strerror (err));
+          if (gpg_err_code (err) == GPG_ERR_CANCELED
+              || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
+            break; /* Don't try the other subkeys.  */
+        }
+    }
+
+ leave:
+  gcry_sexp_release (curve);
+  xfree (cache_nonce);
+  xfree (wrappedkey);
+  xfree (transferkey);
+  gcry_cipher_close (cipherhd);
+  xfree (kek);
+  return err;
+}
+
+
+/* Walk a secret keyblock and produce a public keyblock out of it.
+   Returns a new node or NULL on error. */
+static kbnode_t
+sec_to_pub_keyblock (kbnode_t sec_keyblock)
+{
+  kbnode_t pub_keyblock = NULL;
+  kbnode_t ctx = NULL;
+  kbnode_t secnode, pubnode;
+
+  while ((secnode = walk_kbnode (sec_keyblock, &ctx, 0)))
+    {
+      if (secnode->pkt->pkttype == PKT_SECRET_KEY
+          || secnode->pkt->pkttype == PKT_SECRET_SUBKEY)
+       {
+         /* Make a public key.  */
+         PACKET *pkt;
+          PKT_public_key *pk;
+
+         pkt = xtrycalloc (1, sizeof *pkt);
+          pk = pkt? copy_public_key (NULL, secnode->pkt->pkt.public_key): NULL;
+          if (!pk)
+            {
+              xfree (pkt);
+             release_kbnode (pub_keyblock);
+              return NULL;
+            }
+         if (secnode->pkt->pkttype == PKT_SECRET_KEY)
+           pkt->pkttype = PKT_PUBLIC_KEY;
+         else
+           pkt->pkttype = PKT_PUBLIC_SUBKEY;
+         pkt->pkt.public_key = pk;
 
-         pubnode=new_kbnode(pkt);
+         pubnode = new_kbnode (pkt);
        }
       else
        {
-         pubnode=clone_kbnode(secnode);
+         pubnode = clone_kbnode (secnode);
        }
 
-      if(pub_keyblock==NULL)
-       pub_keyblock=pubnode;
+      if (!pub_keyblock)
+       pub_keyblock = pubnode;
       else
-       add_kbnode(pub_keyblock,pubnode);
+       add_kbnode (pub_keyblock, pubnode);
     }
 
   return pub_keyblock;
@@ -1089,127 +1537,148 @@ sec_to_pub_keyblock(KBNODE sec_keyblock)
  * with the trust calculation.
  */
 static int
-import_secret_one( const char *fname, KBNODE keyblock, 
-                   struct stats_s *stats, unsigned int options)
+import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock,
+                   struct stats_s *stats, int batch, unsigned int options,
+                   int for_migration,
+                   import_screener_t screener, void *screener_arg)
 {
-    PKT_secret_key *sk;
-    KBNODE node, uidnode;
-    u32 keyid[2];
-    int rc = 0;
+  PKT_public_key *pk;
+  struct seckey_info *ski;
+  KBNODE node, uidnode;
+  u32 keyid[2];
+  int rc = 0;
+  int nr_prev;
+  kbnode_t pub_keyblock;
+  char pkstrbuf[PUBKEY_STRING_SIZE];
 
-    /* get the key and print some info about it */
-    node = find_kbnode( keyblock, PKT_SECRET_KEY );
-    if( !node )
-       BUG();
+  /* 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 );
+  pk = node->pkt->pkt.public_key;
 
-    if( opt.verbose )
-      {
-       log_info( "sec  %4u%c/%s %s   ",
-                 nbits_from_sk( sk ),
-                 pubkey_letter( sk->pubkey_algo ),
-                 keystr_from_sk(sk), datestr_from_sk(sk) );
-       if( uidnode )
-         print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
-                            uidnode->pkt->pkt.user_id->len );
-       log_printf ("\n");
-      }
-    stats->secret_read++;
+  keyid_from_pk (pk, keyid);
+  uidnode = find_next_kbnode (keyblock, PKT_USER_ID);
 
-    if( !uidnode )
-      {
-       log_error( _("key %s: no user ID\n"), keystr_from_sk(sk));
-       return 0;
-      }
+  if (screener && screener (keyblock, screener_arg))
+    {
+      log_error (_("secret key %s: %s\n"), keystr_from_pk (pk),
+                 _("rejected by import screener"));
+      return 0;
+  }
 
-    if(sk->protect.algo>110)
-      {
-       log_error(_("key %s: secret key with invalid cipher %d"
-                   " - skipped\n"),keystr_from_sk(sk),sk->protect.algo);
-       return 0;
-      }
+  if (opt.verbose && !for_migration)
+    {
+      log_info ("sec  %s/%s %s   ",
+                pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
+                keystr_from_pk (pk), datestr_from_pk (pk));
+      if (uidnode)
+        print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name,
+                           uidnode->pkt->pkt.user_id->len);
+      log_printf ("\n");
+    }
+  stats->secret_read++;
 
-#ifdef ENABLE_SELINUX_HACKS
-    if (1)
-      {
-        /* We don't allow to import secret keys because that may be used
-           to put a secret key into the keyring and the user might later
-           be tricked into signing stuff with that key.  */
+  if ((options & IMPORT_NO_SECKEY))
+    {
+      if (!for_migration)
         log_error (_("importing secret keys not allowed\n"));
-        return 0;
-      }
-#endif 
-    
-    clear_kbnode_flags( keyblock );
+      return 0;
+    }
 
-    /* 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);
+  if (!uidnode)
+    {
+      if (!for_migration)
+        log_error( _("key %s: no user ID\n"), keystr_from_pk (pk));
+      return 0;
+    }
 
-       /* 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 %s: secret key imported\n"), keystr_from_sk(sk));
-       stats->secret_imported++;
-        if (is_status_enabled ()) 
-         print_import_ok (NULL, sk, 1|16);
+  ski = pk->seckey_info;
+  if (!ski)
+    {
+      /* Actually an internal error.  */
+      log_error ("key %s: secret key info missing\n", keystr_from_pk (pk));
+      return 0;
+    }
 
-       if(options&IMPORT_SK2PK)
-         {
-           /* Try and make a public key out of this. */
+  /* A quick check to not import keys with an invalid protection
+     cipher algorithm (only checks the primary key, though).  */
+  if (ski->algo > 110)
+    {
+      if (!for_migration)
+        log_error (_("key %s: secret key with invalid cipher %d"
+                     " - skipped\n"), keystr_from_pk (pk), ski->algo);
+      return 0;
+    }
 
-           KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
-           if(pub_keyblock)
-             {
-               import_one(fname,pub_keyblock,stats,
-                          NULL,NULL,opt.import_options,1);
-               release_kbnode(pub_keyblock);
-             }
-         }
+#ifdef ENABLE_SELINUX_HACKS
+  if (1)
+    {
+      /* We don't allow to import secret keys because that may be used
+         to put a secret key into the keyring and the user might later
+         be tricked into signing stuff with that key.  */
+      log_error (_("importing secret keys not allowed\n"));
+      return 0;
+    }
+#endif
 
-       /* Now that the key is definitely incorporated into the keydb,
-          if we have the public part of this key, we need to check if
-          the prefs are rational. */
-       node=get_pubkeyblock(keyid);
-       if(node)
-         {
-           check_prefs(node);
-           release_kbnode(node);
-         }
-      }
-    else if( !rc )
-      { /* we can't merge secret keys */
-       log_error( _("key %s: already in secret keyring\n"),
-                  keystr_from_sk(sk));
-       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 %s: secret key not found: %s\n"),
-                keystr_from_sk(sk), g10_errstr(rc));
+  clear_kbnode_flags (keyblock);
 
-    return rc;
+  nr_prev = stats->skipped_new_keys;
+
+  /* Make a public key out of the key. */
+  pub_keyblock = sec_to_pub_keyblock (keyblock);
+  if (!pub_keyblock)
+    log_error ("key %s: failed to create public key from secret key\n",
+                   keystr_from_pk (pk));
+  else
+    {
+      /* Note that this outputs an IMPORT_OK status message for the
+        public key block, and below we will output another one for
+        the secret keys.  FIXME?  */
+      import_one (ctrl, fname, pub_keyblock, stats,
+                 NULL, NULL, options, 1, for_migration,
+                  screener, screener_arg);
+
+      /* Fixme: We should check for an invalid keyblock and
+        cancel the secret key import in this case.  */
+      release_kbnode (pub_keyblock);
+
+      /* At least we cancel the secret key import when the public key
+        import was skipped due to MERGE_ONLY option and a new
+        key.  */
+      if (stats->skipped_new_keys <= nr_prev)
+       {
+          /* Read the keyblock again to get the effects of a merge.  */
+          /* Fixme: we should do this based on the fingerprint or
+             even better let import_one return the merged
+             keyblock.  */
+          node = get_pubkeyblock (keyid);
+          if (!node)
+            log_error ("key %s: failed to re-lookup public key\n",
+                       keystr_from_pk (pk));
+          else
+            {
+             nr_prev = stats->secret_imported;
+              if (!transfer_secret_keys (ctrl, stats, keyblock, batch))
+                {
+                 int status = 16;
+                  if (!opt.quiet)
+                    log_info (_("key %s: secret key imported\n"),
+                              keystr_from_pk (pk));
+                 if (stats->secret_imported > nr_prev)
+                   status |= 1;
+                  if (is_status_enabled ())
+                    print_import_ok (pk, status);
+                  check_prefs (ctrl, node);
+                }
+              release_kbnode (node);
+            }
+        }
+    }
+
+  return rc;
 }
 
 
@@ -1225,6 +1694,8 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     u32 keyid[2];
     int rc = 0;
 
+    (void)fname;
+
     assert( !node->next );
     assert( node->pkt->pkttype == PKT_SIGNATURE );
     assert( node->pkt->pkt.signature->sig_class == 0x20 );
@@ -1249,13 +1720,13 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
       }
 
     /* read the original keyblock */
-    hd = keydb_new (0);
+    hd = keydb_new ();
     {
         byte afp[MAX_FINGERPRINT_LEN];
         size_t an;
-        
+
         fingerprint_from_pk (pk, afp, &an);
-        while (an < MAX_FINGERPRINT_LEN) 
+        while (an < MAX_FINGERPRINT_LEN)
             afp[an++] = 0;
         rc = keydb_search_fpr (hd, afp);
     }
@@ -1304,7 +1775,7 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     /* and write the keyblock back */
     rc = keydb_update_keyblock (hd, keyblock );
     if (rc)
-       log_error (_("error writing keyring `%s': %s\n"),
+       log_error (_("error writing keyring '%s': %s\n"),
                    keydb_get_resource_name (hd), g10_errstr(rc) );
     keydb_release (hd); hd = NULL;
     /* we are ready */
@@ -1333,8 +1804,8 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
 }
 
 
-/****************
- * loop over the keyblock and 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
@@ -1343,173 +1814,198 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
  * in this keyblock.
  */
 static int
-chk_self_sigs( const char *fname, KBNODE keyblock,
+chk_self_sigs (const char *fname, kbnode_t keyblock,
               PKT_public_key *pk, u32 *keyid, int *non_self )
 {
-    KBNODE n,knode=NULL;
-    PKT_signature *sig;
-    int rc;
-    u32 bsdate=0,rsdate=0;
-    KBNODE bsnode=NULL,rsnode=NULL;
+  kbnode_t n, knode = NULL;
+  PKT_signature *sig;
+  int rc;
+  u32 bsdate=0, rsdate=0;
+  kbnode_t bsnode = NULL, rsnode = NULL;
+
+  (void)fname;
+  (void)pk;
 
-    for( n=keyblock; (n = find_next_kbnode(n, 0)); ) {
-      if(n->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+  for (n=keyblock; (n = find_next_kbnode (n, 0)); )
+    {
+      if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
-         knode=n;
-         bsdate=0;
-         rsdate=0;
-         bsnode=NULL;
-         rsnode=NULL;
+         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 ( n->pkt->pkttype != PKT_SIGNATURE )
+        continue;
 
-           if( IS_UID_SIG(sig) || IS_UID_REV(sig) )
-             {
-               KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
-               if( !unode )
-                 {
-                   log_error( _("key %s: no user ID for signature\n"),
-                              keystr(keyid));
-                   return -1;  /* the complete keyblock is invalid */
-                 }
+      sig = n->pkt->pkt.signature;
+      if ( keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1] )
+        {
+          *non_self = 1;
+          continue;
+        }
 
-               /* 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 %s: unsupported public key "
-                                     "algorithm on user ID \"%s\"\n"):
-                                   _("key %s: invalid self-signature "
-                                     "on user ID \"%s\"\n"),
-                                   keystr(keyid),p);
-                         xfree(p);
-                       }
-                   }
-                 else
-                   unode->flag |= 1; /* mark that signature checked */
-               }
-             }
-           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. */
+      /* 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( !knode )
-                 {
-                   if(opt.verbose)
-                     log_info( _("key %s: no subkey for key binding\n"),
-                               keystr(keyid));
-                   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 %s: unsupported public key"
-                                    " algorithm\n"):
-                                  _("key %s: invalid subkey binding\n"),
-                                  keystr(keyid));
-                       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 %s: removed multiple subkey"
-                                          " binding\n"),keystr(keyid));
-                           }
+      if ( IS_UID_SIG(sig) || IS_UID_REV(sig) )
+        {
+          KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
+          if ( !unode )
+            {
+              log_error( _("key %s: no user ID for signature\n"),
+                         keystr(keyid));
+              return -1;  /* The complete keyblock is invalid.  */
+            }
 
-                         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 %s: no subkey for key revocation\n"),
-                               keystr(keyid));
-                   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 %s: unsupported public"
-                                    " key algorithm\n"):
-                                  _("key %s: invalid subkey revocation\n"),
-                                  keystr(keyid));
-                       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 %s: removed multiple subkey"
-                                            " revocation\n"),keystr(keyid));
-                             }
-
-                           rsnode=n;
-                           rsdate=sig->timestamp;
-                         }
-                       else
-                         n->flag|=4; /* older */
-                     }
-                 }
-           }
-       }
-       else
-         *non_self=1;
+          /* 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 (gpg_err_code(rc) == G10ERR_PUBKEY_ALGO ?
+                                _("key %s: unsupported public key "
+                                  "algorithm on user ID \"%s\"\n"):
+                                _("key %s: invalid self-signature "
+                                  "on user ID \"%s\"\n"),
+                                keystr (keyid),p);
+                      xfree (p);
+                    }
+                }
+              else
+                unode->flag |= 1; /* Mark that signature checked. */
+            }
+        }
+      else if (IS_KEY_SIG (sig))
+        {
+          rc = check_key_signature (keyblock, n, NULL);
+          if ( rc )
+            {
+              if (opt.verbose)
+                log_info (gpg_err_code (rc) == G10ERR_PUBKEY_ALGO ?
+                          _("key %s: unsupported public key algorithm\n"):
+                          _("key %s: invalid direct key signature\n"),
+                          keystr (keyid));
+              n->flag |= 4;
+            }
+        }
+      else if ( IS_SUBKEY_SIG (sig) )
+        {
+          /* 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 %s: no subkey for key binding\n"),
+                          keystr (keyid));
+              n->flag |= 4; /* delete this */
+            }
+          else
+            {
+              rc = check_key_signature (keyblock, n, NULL);
+              if ( rc )
+                {
+                  if (opt.verbose)
+                    log_info (gpg_err_code (rc) == G10ERR_PUBKEY_ALGO ?
+                              _("key %s: unsupported public key"
+                                " algorithm\n"):
+                              _("key %s: invalid subkey binding\n"),
+                              keystr (keyid));
+                  n->flag |= 4;
+                }
+              else
+                {
+                  /* It's valid, so is it newer? */
+                  if (sig->timestamp >= bsdate)
+                    {
+                      knode->flag |= 1;  /* The subkey is valid.  */
+                      if (bsnode)
+                        {
+                          /* Delete the last binding sig since this
+                             one is newer */
+                          bsnode->flag |= 4;
+                          if (opt.verbose)
+                            log_info (_("key %s: removed multiple subkey"
+                                        " binding\n"),keystr(keyid));
+                        }
+
+                      bsnode = n;
+                      bsdate = sig->timestamp;
+                    }
+                  else
+                    n->flag |= 4; /* older */
+                }
+            }
+        }
+      else if ( IS_SUBKEY_REV (sig) )
+        {
+          /* 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 %s: no subkey for key revocation\n"),
+                          keystr(keyid));
+              n->flag |= 4; /* delete this */
+            }
+          else
+            {
+              rc = check_key_signature (keyblock, n, NULL);
+              if ( rc )
+                {
+                  if(opt.verbose)
+                    log_info (gpg_err_code (rc) == G10ERR_PUBKEY_ALGO ?
+                              _("key %s: unsupported public"
+                                " key algorithm\n"):
+                              _("key %s: invalid subkey revocation\n"),
+                              keystr(keyid));
+                  n->flag |= 4;
+                }
+              else
+                {
+                  /* It's valid, so is it newer? */
+                  if (sig->timestamp >= rsdate)
+                    {
+                      if (rsnode)
+                        {
+                          /* Delete the last revocation sig since
+                             this one is newer.  */
+                          rsnode->flag |= 4;
+                          if (opt.verbose)
+                            log_info (_("key %s: removed multiple subkey"
+                                        " revocation\n"),keystr(keyid));
+                        }
+
+                      rsnode = n;
+                      rsdate = sig->timestamp;
+                    }
+                  else
+                    n->flag |= 4; /* older */
+                }
+            }
+        }
     }
 
-    return 0;
+  return 0;
 }
 
+
 /****************
  * delete all parts which are invalid and those signatures whose
  * public key algorithm is not available in this implemenation;
@@ -1524,6 +2020,8 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
     KBNODE node;
     int nvalid=0, uid_seen=0, subkey_seen=0;
 
+    (void)fname;
+
     for(node=keyblock->next; node; node = node->next ) {
        if( node->pkt->pkttype == PKT_USER_ID ) {
            uid_seen = 1;
@@ -1573,7 +2071,7 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
        else if( node->pkt->pkttype == PKT_SIGNATURE &&
                 !node->pkt->pkt.signature->flags.exportable &&
                 !(options&IMPORT_LOCAL_SIGS) &&
-                seckey_available( node->pkt->pkt.signature->keyid ) )
+                !have_secret_key_with_kid (node->pkt->pkt.signature->keyid))
          {
            /* here we violate the rfc a bit by still allowing
             * to import non-exportable signature when we have the
@@ -1661,11 +2159,17 @@ collapse_uids( KBNODE *keyblock )
     {
       KBNODE uid2;
 
+      if(is_deleted_kbnode(uid1))
+       continue;
+
       if(uid1->pkt->pkttype!=PKT_USER_ID)
        continue;
 
       for(uid2=uid1->next;uid2;uid2=uid2->next)
        {
+         if(is_deleted_kbnode(uid2))
+           continue;
+
          if(uid2->pkt->pkttype!=PKT_USER_ID)
            continue;
 
@@ -1681,6 +2185,9 @@ collapse_uids( KBNODE *keyblock )
                 uid1 */
              for(last=uid2;last->next;last=last->next)
                {
+                 if(is_deleted_kbnode(last))
+                   continue;
+
                  if(last->next->pkt->pkttype==PKT_USER_ID
                     || last->next->pkt->pkttype==PKT_PUBLIC_SUBKEY
                     || last->next->pkt->pkttype==PKT_SECRET_SUBKEY)
@@ -1693,13 +2200,16 @@ collapse_uids( KBNODE *keyblock )
              /* Now put uid2 in place as part of uid1 */
              last->next=uid1->next;
              uid1->next=uid2;
-             remove_kbnode(keyblock,uid2);
+             delete_kbnode(uid2);
 
              /* Now dedupe uid1 */
              for(sig1=uid1->next;sig1;sig1=sig1->next)
                {
                  KBNODE sig2;
 
+                 if(is_deleted_kbnode(sig1))
+                   continue;
+
                  if(sig1->pkt->pkttype==PKT_USER_ID
                     || sig1->pkt->pkttype==PKT_PUBLIC_SUBKEY
                     || sig1->pkt->pkttype==PKT_SECRET_SUBKEY)
@@ -1710,6 +2220,9 @@ collapse_uids( KBNODE *keyblock )
 
                  for(sig2=sig1->next,last=sig1;sig2;last=sig2,sig2=sig2->next)
                    {
+                     if(is_deleted_kbnode(sig2))
+                       continue;
+
                      if(sig2->pkt->pkttype==PKT_USER_ID
                         || sig2->pkt->pkttype==PKT_PUBLIC_SUBKEY
                         || sig2->pkt->pkttype==PKT_SECRET_SUBKEY)
@@ -1723,7 +2236,7 @@ collapse_uids( KBNODE *keyblock )
                        {
                          /* We have a match, so delete the second
                             signature */
-                         remove_kbnode(&uid1,sig2);
+                         delete_kbnode(sig2);
                          sig2=last;
                        }
                    }
@@ -1732,16 +2245,18 @@ collapse_uids( KBNODE *keyblock )
        }
     }
 
+  commit_kbnode(keyblock);
+
   if(any && !opt.quiet)
     {
       const char *key="???";
 
-      if( (uid1=find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
-       key=keystr_from_pk(uid1->pkt->pkt.public_key);
-      else if( (uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
-       key=keystr_from_sk(uid1->pkt->pkt.secret_key);
+      if ((uid1 = find_kbnode (*keyblock, PKT_PUBLIC_KEY)) )
+       key = keystr_from_pk (uid1->pkt->pkt.public_key);
+      else if ((uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY)) )
+       key = keystr_from_pk (uid1->pkt->pkt.public_key);
 
-      log_info(_("key %s: duplicated user ID detected - merged\n"),key);
+      log_info (_("key %s: duplicated user ID detected - merged\n"), key);
     }
 
   return any;
@@ -1751,10 +2266,10 @@ collapse_uids( KBNODE *keyblock )
    present.  This may be called without the benefit of merge_xxxx so
    you can't rely on pk->revkey and friends. */
 static void
-revocation_present(KBNODE keyblock)
+revocation_present (ctrl_t ctrl, kbnode_t keyblock)
 {
-  KBNODE onode,inode;
-  PKT_public_key *pk=keyblock->pkt->pkt.public_key;
+  kbnode_t onode, inode;
+  PKT_public_key *pk = keyblock->pkt->pkt.public_key;
 
   for(onode=keyblock->next;onode;onode=onode->next)
     {
@@ -1806,9 +2321,10 @@ revocation_present(KBNODE keyblock)
                              log_info(_("WARNING: key %s may be revoked:"
                                         " fetching revocation key %s\n"),
                                       tempkeystr,keystr(keyid));
-                             keyserver_import_fprint(sig->revkey[idx]->fpr,
-                                                     MAX_FINGERPRINT_LEN,
-                                                     opt.keyserver);
+                             keyserver_import_fprint (ctrl,
+                                                       sig->revkey[idx]->fpr,
+                                                       MAX_FINGERPRINT_LEN,
+                                                       opt.keyserver);
 
                              /* Do we have it now? */
                              rc=get_pubkey_byfprint_fast (NULL,
@@ -1972,8 +2488,8 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
            /* 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 ) )
+                   && !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);
@@ -1991,14 +2507,11 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                 || 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
+                if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                     || node->pkt->pkttype == PKT_SECRET_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 );
@@ -2017,11 +2530,14 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
  * append the userid starting with NODE and all signatures to KEYBLOCK.
  */
 static int
-append_uidKBNODE keyblock, KBNODE node, int *n_sigs,
-                                         const char *fname, u32 *keyid )
+append_uid (KBNODE keyblock, KBNODE node, int *n_sigs,
+            const char *fname, u32 *keyid )
 {
     KBNODE n, n_where=NULL;
 
+    (void)fname;
+    (void)keyid;
+
     assert(node->pkt->pkttype == PKT_USER_ID );
 
     /* find the position */
@@ -2069,6 +2585,9 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
     KBNODE n, n2;
     int found=0;
 
+    (void)fname;
+    (void)keyid;
+
     assert(dst->pkt->pkttype == PKT_USER_ID );
     assert(src->pkt->pkttype == PKT_USER_ID );
 
@@ -2104,12 +2623,15 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
  * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY.
  */
 static int
-merge_keysigsKBNODE dst, KBNODE src, int *n_sigs,
-                                   const char *fname, u32 *keyid )
+merge_keysigs (KBNODE dst, KBNODE src, int *n_sigs,
+               const char *fname, u32 *keyid)
 {
     KBNODE n, n2;
     int found=0;
 
+    (void)fname;
+    (void)keyid;
+
     assert(   dst->pkt->pkttype == PKT_PUBLIC_SUBKEY
           || dst->pkt->pkttype == PKT_SECRET_SUBKEY );
 
@@ -2152,16 +2674,20 @@ merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
     return 0;
 }
 
-/****************
- * append the subkey starting with NODE and all signatures to KEYBLOCK.
+
+/*
+ * 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_keyKBNODE keyblock, KBNODE node, int *n_sigs,
-                                         const char *fname, u32 *keyid )
+append_key (KBNODE keyblock, KBNODE node, int *n_sigs,
+            const char *fname, u32 *keyid)
 {
     KBNODE n;
 
+    (void)fname;
+    (void)keyid;
+
     assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
           || node->pkt->pkttype == PKT_SECRET_SUBKEY );
 
@@ -2182,232 +2708,3 @@ append_key( KBNODE keyblock, KBNODE node, int *n_sigs,
 
     return 0;
 }
-
-
-
-/* Walk a public keyblock and produce a secret keyblock out of it.
-   Instead of inserting the secret key parameters (which we don't
-   have), we insert a stub.  */
-static KBNODE
-pub_to_sec_keyblock (KBNODE pub_keyblock)
-{
-  KBNODE pubnode, secnode;
-  KBNODE sec_keyblock = NULL;
-  KBNODE walkctx = NULL;
-
-  while((pubnode = walk_kbnode (pub_keyblock,&walkctx,0)))
-    {
-      if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY
-          || pubnode->pkt->pkttype == PKT_PUBLIC_SUBKEY)
-       {
-         /* Make a secret key.  We only need to convert enough to
-            write the keyblock out. */
-         PKT_public_key *pk = pubnode->pkt->pkt.public_key;
-         PACKET *pkt = xmalloc_clear (sizeof *pkt);
-         PKT_secret_key *sk = xmalloc_clear (sizeof *sk);
-          int i, n;
-          
-          if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY)
-           pkt->pkttype = PKT_SECRET_KEY;
-         else
-           pkt->pkttype = PKT_SECRET_SUBKEY;
-          
-         pkt->pkt.secret_key = sk;
-
-          copy_public_parts_to_secret_key ( pk, sk );
-         sk->version     = pk->version;
-         sk->timestamp   = pk->timestamp;
-        
-          n = pubkey_get_npkey (pk->pubkey_algo);
-          if (!n)
-            n = 1; /* Unknown number of parameters, however the data
-                      is stored in the first mpi. */
-          for (i=0; i < n; i++ )
-            sk->skey[i] = mpi_copy (pk->pkey[i]);
-  
-          sk->is_protected = 1;
-          sk->protect.s2k.mode = 1001;
-  
-         secnode = new_kbnode (pkt);
-        }
-      else
-       {
-         secnode = clone_kbnode (pubnode);
-       }
-      
-      if(!sec_keyblock)
-       sec_keyblock = secnode;
-      else
-       add_kbnode (sec_keyblock, secnode);
-    }
-
-  return sec_keyblock;
-}
-
-
-/* Walk over the secret keyring SEC_KEYBLOCK and update any simple
-   stub keys with the serial number SNNUM of the card if one of the
-   fingerprints FPR1, FPR2 or FPR3 match.  Print a note if the key is
-   a duplicate (may happen in case of backed uped keys). 
-   
-   Returns: True if anything changed.
-*/
-static int
-update_sec_keyblock_with_cardinfo (KBNODE sec_keyblock, 
-                                   const unsigned char *fpr1,
-                                   const unsigned char *fpr2,
-                                   const unsigned char *fpr3,
-                                   const char *serialnostr)
-{
-  KBNODE node;
-  KBNODE walkctx = NULL;
-  PKT_secret_key *sk;
-  byte array[MAX_FINGERPRINT_LEN];
-  size_t n;
-  int result = 0;
-  const char *s;
-
-  while((node = walk_kbnode (sec_keyblock, &walkctx, 0)))
-    {
-      if (node->pkt->pkttype != PKT_SECRET_KEY
-          && node->pkt->pkttype != PKT_SECRET_SUBKEY)
-        continue;
-      sk = node->pkt->pkt.secret_key;
-      
-      fingerprint_from_sk (sk, array, &n);
-      if (n != 20)
-        continue; /* Can't be a card key.  */
-      if ( !((fpr1 && !memcmp (array, fpr1, 20))
-             || (fpr2 && !memcmp (array, fpr2, 20))
-             || (fpr3 && !memcmp (array, fpr3, 20))) )
-        continue;  /* No match.  */
-
-      if (sk->is_protected == 1 && sk->protect.s2k.mode == 1001)
-        {
-          /* Standard case: migrate that stub to a key stub.  */
-          sk->protect.s2k.mode = 1002;
-          s = serialnostr;
-          for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
-               sk->protect.ivlen++, s += 2)
-            sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s);
-          result = 1;
-        }
-      else if (sk->is_protected == 1 && sk->protect.s2k.mode == 1002)
-        {
-          s = serialnostr;
-          for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
-               sk->protect.ivlen++, s += 2)
-            if (sk->protect.iv[sk->protect.ivlen] != xtoi_2 (s))
-              {
-                log_info (_("NOTE: a key's S/N does not "
-                            "match the card's one\n"));
-                break;
-              }
-        }
-      else
-        {
-          if (node->pkt->pkttype != PKT_SECRET_KEY)
-            log_info (_("NOTE: primary key is online and stored on card\n"));
-          else
-            log_info (_("NOTE: secondary key is online and stored on card\n"));
-        }
-    }
-
-  return result;
-}
-
-
-
-/* Check whether a secret key stub exists for the public key PK.  If
-   not create such a stub key and store it into the secring.  If it
-   exists, add appropriate subkey stubs and update the secring.
-   Return 0 if the key could be created. */
-int
-auto_create_card_key_stub ( const char *serialnostr, 
-                            const unsigned char *fpr1,
-                            const unsigned char *fpr2,
-                            const unsigned char *fpr3)
-{
-  KBNODE pub_keyblock;
-  KBNODE sec_keyblock;
-  KEYDB_HANDLE hd;
-  int rc;
-
-  /* We only want to do this for an OpenPGP card.  */
-  if (!serialnostr || strncmp (serialnostr, "D27600012401", 12) 
-      || strlen (serialnostr) != 32 )
-    return G10ERR_GENERAL;
-
-  /* First get the public keyring from any of the provided fingerprints. */
-  if ( (fpr1 && !get_keyblock_byfprint (&pub_keyblock, fpr1, 20))
-       || (fpr2 && !get_keyblock_byfprint (&pub_keyblock, fpr2, 20))
-       || (fpr3 && !get_keyblock_byfprint (&pub_keyblock, fpr3, 20)))
-    ;
-  else
-    return G10ERR_GENERAL;
-  hd = keydb_new (1);
-
-  /* Now check whether there is a secret keyring.  */
-  {
-    PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key;
-    byte afp[MAX_FINGERPRINT_LEN];
-    size_t an;
-
-    fingerprint_from_pk (pk, afp, &an);
-    memset (afp, 0, MAX_FINGERPRINT_LEN);
-    rc = keydb_search_fpr (hd, afp);
-  }
-
-  if (!rc)
-    {
-      rc = keydb_get_keyblock (hd, &sec_keyblock);
-      if (rc)
-        {
-          log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
-          rc = G10ERR_GENERAL;
-        }
-      else
-        {
-          merge_keys_and_selfsig (sec_keyblock);
-          
-          /* FIXME: We need to add new subkeys first.  */
-          if (update_sec_keyblock_with_cardinfo (sec_keyblock,
-                                                 fpr1, fpr2, fpr3,
-                                                 serialnostr))
-            {
-              rc = keydb_update_keyblock (hd, sec_keyblock );
-              if (rc)
-                log_error (_("error writing keyring `%s': %s\n"),
-                           keydb_get_resource_name (hd), g10_errstr(rc) );
-            }
-        }
-    }
-  else  /* A secret key does not exists - create it.  */
-    {
-      sec_keyblock = pub_to_sec_keyblock (pub_keyblock);
-      update_sec_keyblock_with_cardinfo (sec_keyblock,
-                                         fpr1, fpr2, fpr3,
-                                         serialnostr);
-
-      rc = keydb_locate_writable (hd, NULL);
-      if (rc)
-        {
-          log_error (_("no default secret keyring: %s\n"), g10_errstr (rc));
-          rc = G10ERR_GENERAL;
-        }
-      else
-        {
-          rc = keydb_insert_keyblock (hd, sec_keyblock );
-          if (rc)
-            log_error (_("error writing keyring `%s': %s\n"),
-                       keydb_get_resource_name (hd), g10_errstr(rc) );
-        }
-    }
-    
-  release_kbnode (sec_keyblock);
-  release_kbnode (pub_keyblock);
-  keydb_release (hd);
-  return rc;
-}
-