speedo: Improve speedo Makefile.
[gnupg.git] / g10 / import.c
index 13773da..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, 2010 Free Software Foundation, Inc.
+ *               2007, 2010, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2014  Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -62,14 +63,19 @@ struct stats_s {
 
 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);
+                  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 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, unsigned int options);
+                              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,
@@ -112,7 +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},
+      {"convert-sk-to-pk",0, NULL,NULL}, /* Not anymore needed due to
+                                            the new design.  */
       {NULL,0,NULL,NULL}
     };
 
@@ -165,7 +172,8 @@ import_release_stats_handle (void *p)
 static int
 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;
@@ -174,7 +182,8 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames,
         stats = import_new_stats_handle ();
 
     if (inp) {
-      rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options);
+      rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options,
+                   screener, screener_arg);
     }
     else {
         if( !fnames && !nnames )
@@ -192,16 +201,17 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames,
                 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 (ctrl, 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, IOBUF_IOCTL_INVALIDATE_CACHE, 
+                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 )
@@ -220,30 +230,60 @@ import_keys_internal (ctrl_t ctrl, iobuf_t 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 (ctrl_t ctrl, char **fnames, int nnames,
             void *stats_handle, unsigned int options )
 {
   import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle,
-                        NULL, NULL, options);
+                        NULL, NULL, options, NULL, NULL);
 }
 
 int
 import_keys_stream (ctrl_t ctrl, IOBUF inp, void *stats_handle,
-                   unsigned char **fpr, size_t *fpr_len,unsigned int options)
+                   unsigned char **fpr, size_t *fpr_len, unsigned int options)
 {
   return import_keys_internal (ctrl, inp, NULL, 0, stats_handle,
-                               fpr, fpr_len, options);
+                               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 (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats,
-       unsigned char **fpr,size_t *fpr_len,unsigned int options )
+       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
@@ -264,9 +304,13 @@ import (ctrl_t ctrl, 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 (ctrl, 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 );
@@ -285,12 +329,64 @@ import (ctrl_t ctrl, 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)
 {
@@ -355,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 )
@@ -432,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
@@ -566,19 +683,16 @@ fix_bad_direct_key_sigs (kbnode_t keyblock, u32 *keyid)
 
 
 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);
@@ -616,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;
 
@@ -646,9 +760,9 @@ check_prefs(KBNODE keyblock)
                {
                  if (openpgp_cipher_test_algo (prefs->value))
                    {
-                     const char *algo = 
+                     const char *algo =
                         (openpgp_cipher_test_algo (prefs->value)
-                         ? num 
+                         ? num
                          : openpgp_cipher_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
@@ -663,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);
@@ -713,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);
        }
@@ -724,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 gpg.
+ * 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;
@@ -744,6 +861,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
     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 );
@@ -755,11 +873,10 @@ 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_buffer (log_get_stream (),
@@ -771,11 +888,19 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
 
     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);
@@ -808,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) )
          {
@@ -821,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;
     }
@@ -833,12 +960,13 @@ 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++;
@@ -848,16 +976,16 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
 
         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
          {
@@ -873,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"),
@@ -885,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 ) )
@@ -900,7 +1028,8 @@ 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;
          }
 
@@ -911,7 +1040,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
             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);
         }
@@ -935,7 +1064,7 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
         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 );
@@ -957,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 )
@@ -1005,17 +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
          {
             same_key = 1;
-            if (is_status_enabled ()) 
-             print_import_ok (pk, NULL, 0);
+            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);
@@ -1063,15 +1191,15 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
 
     if (mod_key)
       {
-       revocation_present (keyblock_orig);
+       revocation_present (ctrl, keyblock_orig);
        if (!from_sk && have_secret_key_with_kid (keyid))
-         check_prefs (keyblock_orig);
+         check_prefs (ctrl, keyblock_orig);
       }
     else if (new_key)
       {
-       revocation_present (keyblock);
+       revocation_present (ctrl, keyblock);
        if (!from_sk && have_secret_key_with_kid (keyid))
-         check_prefs (keyblock);
+         check_prefs (ctrl, keyblock);
       }
 
     release_kbnode( keyblock_orig );
@@ -1082,29 +1210,32 @@ import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
 
 
 /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent.  The
-   function prints diagnostics and returns an error code. */
+   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, kbnode_t sec_keyblock)
+transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock,
+                      int batch)
 {
   gpg_error_t err = 0;
   void *kek = NULL;
   size_t keklen;
   kbnode_t ctx = NULL;
   kbnode_t node;
-  PKT_secret_key *main_sk, *sk;
+  PKT_public_key *main_pk, *pk;
+  struct seckey_info *ski;
   int nskey;
   membuf_t mbuf;
   int i, j;
-  size_t n;
-  void *format_args_buf_ptr[PUBKEY_MAX_NSKEY];
-  int   format_args_buf_int[PUBKEY_MAX_NSKEY];
   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);
@@ -1124,18 +1255,51 @@ transfer_secret_keys (ctrl_t ctrl, kbnode_t sec_keyblock)
   xfree (kek);
   kek = NULL;
 
-  main_sk = NULL;
+  main_pk = NULL;
   while ((node = walk_kbnode (sec_keyblock, &ctx, 0)))
     {
       if (node->pkt->pkttype != PKT_SECRET_KEY
           && node->pkt->pkttype != PKT_SECRET_SUBKEY)
         continue;
-      sk = node->pkt->pkt.secret_key;
-      if (!main_sk)
-        main_sk = sk;
+      pk = node->pkt->pkt.public_key;
+      if (!main_pk)
+        main_pk = pk;
+
+      /* 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];
+        }
+
+
+      ski = pk->seckey_info;
+      if (!ski)
+        BUG ();
+
+      stats->count++;
+      stats->secret_read++;
+
+      /* 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;
 
       /* Convert our internal secret key object into an S-expression.  */
-      nskey = pubkey_get_nskey (sk->pubkey_algo);
+      nskey = pubkey_get_nskey (pk->pubkey_algo);
       if (!nskey || nskey > PUBKEY_MAX_NSKEY)
         {
           err = gpg_error (GPG_ERR_BAD_SECKEY);
@@ -1145,53 +1309,90 @@ transfer_secret_keys (ctrl_t ctrl, kbnode_t sec_keyblock)
 
       init_membuf (&mbuf, 50);
       put_membuf_str (&mbuf, "(skey");
-      for (i=j=0; i < nskey; i++)
+      if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
+          || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
+          || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
         {
-          if (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE))
+          /* The ECC case.  */
+          char *curvestr = openpgp_oid_to_str (pk->pkey[0]);
+          if (!curvestr)
+            err = gpg_error_from_syserror ();
+          else
             {
-              put_membuf_str (&mbuf, " e %b");
-              format_args_buf_ptr[i] = gcry_mpi_get_opaque (sk->skey[i], &n);
-              format_args_buf_int[i] = (n+7)/8;
-              format_args[j++] = format_args_buf_int + i;
-              format_args[j++] = format_args_buf_ptr + i;
+              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
+        }
+      else
+        {
+          /* Standard case for the old (non-ECC) algorithms.  */
+          for (i=j=0; i < nskey; i++)
             {
-              put_membuf_str (&mbuf, " _ %m");
-              format_args[j++] = sk->skey + 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, ")\n");
+      put_membuf_str (&mbuf, ")");
       put_membuf (&mbuf, "", 1);
-      {
-        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)
+        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 (sk->is_protected)
+      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)sk->protect.s2k.count);
+                    (unsigned long)ski->s2k.count);
           err = gcry_sexp_build
             (&prot, NULL,
              " (protection %s %s %b %d %s %b %s)\n",
-             sk->protect.sha1chk? "sha1":"sum",
-             openpgp_cipher_algo_name (sk->protect.algo),
-             (int)sk->protect.ivlen, sk->protect.iv,
-             sk->protect.s2k.mode,
-             openpgp_md_algo_name (sk->protect.s2k.hash_algo),
-             (int)sizeof (sk->protect.s2k.salt), sk->protect.s2k.salt,
+             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
@@ -1205,12 +1406,13 @@ transfer_secret_keys (ctrl_t ctrl, kbnode_t sec_keyblock)
                                "(openpgp-private-key\n"
                                " (version %d)\n"
                                " (algo %s)\n"
-                               " %S\n"
+                               " %S%S\n"
                                " (csum %d)\n"
                                " %S)\n",
-                               sk->version,
-                               openpgp_pk_algo_name (sk->pubkey_algo),
-                               skey, (int)(unsigned long)sk->csum, prot);
+                               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)
@@ -1237,74 +1439,41 @@ transfer_secret_keys (ctrl_t ctrl, kbnode_t sec_keyblock)
       transferkey = NULL;
 
       /* Send the wrapped key to the agent.  */
-      { 
-        char *uid, *desc;
-        size_t uidlen;
-        u32 keyid[2];
-        char *orig_codeset;
-        
-        keyid_from_sk (sk, keyid);
-        uid = get_user_id (keyid, &uidlen); 
-        orig_codeset = i18n_switchto_utf8 ();
-        desc = xtryasprintf (_("Please enter the passphrase to import the"
-                             " secret key for the OpenPGP certificate:\n"
-                               "\"%.*s\"\n"             \
-                               "%u-bit %s key, ID %s,\n"       
-                               "created %s.\n"),
-                             (int)uidlen, uid,
-                             nbits_from_sk (sk),
-                             openpgp_pk_algo_name (sk->pubkey_algo),
-                             (main_sk == sk
-                              ? keystr_from_sk (sk)
-                              : keystr_from_sk_with_sub (main_sk, sk)),
-                             strtimestamp (sk->timestamp));
-        i18n_switchback (orig_codeset);
-        xfree (uid);
-        if (desc)
-          {
-            uid = percent_plus_escape (desc);
-            xfree (desc);
-            desc = uid;
-          }
-        err = agent_import_key (ctrl, desc, wrappedkey, wrappedkeylen);
+      {
+        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_sk_with_sub (main_sk, sk));
-          /* stats->count++; */
-          /* stats->secret_read++; */
-          /* stats->secret_imported++; */
+                      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_sk_with_sub (main_sk, sk));
+                      keystr_from_pk_with_sub (main_pk, pk));
           err = 0;
-          /* stats->count++; */
-          /* stats->secret_read++; */
-          /* stats->secret_dups++; */
+          stats->secret_dups++;
         }
       else
         {
           log_error (_("key %s: error sending to agent: %s\n"),
-                     keystr_from_sk_with_sub (main_sk, sk),
+                     keystr_from_pk_with_sub (main_pk, pk),
                      gpg_strerror (err));
-          if (sk->protect.algo == GCRY_CIPHER_IDEA
-              && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO)
-            {
-              write_status (STATUS_RSA_OR_IDEA);
-              idea_cipher_warn (0);
-            }
-          if (gpg_err_code (err) == GPG_ERR_CANCELED)
+          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);
@@ -1327,40 +1496,24 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock)
       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 a public key.  */
          PACKET *pkt;
-         PKT_secret_key *sk;
-         PKT_public_key *pk;
-         int n, i;
-
-         pkt = xcalloc (1, sizeof *pkt);
-         sk = secnode->pkt->pkt.secret_key;
-         pk = xcalloc (1, sizeof *pk);
+          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;
 
-         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)
-           {
-             /* We can't properly extract the pubkey without knowing
-                the number of MPIs */
-             release_kbnode (pub_keyblock);
-             return NULL;
-           }
-
-          for (i=0; i < n; i++)
-            pk->pkey[i] = mpi_copy (sk->skey[i]);
          pubnode = new_kbnode (pkt);
        }
       else
@@ -1384,49 +1537,78 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock)
  * with the trust calculation.
  */
 static int
-import_secret_one (ctrl_t ctrl, 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;
+  PKT_public_key *pk;
+  struct seckey_info *ski;
   KBNODE node, uidnode;
   u32 keyid[2];
-  int have_seckey;
   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 ();
-  
-  sk = node->pkt->pkt.secret_key;
-  keyid_from_sk (sk, keyid);
+
+  pk = node->pkt->pkt.public_key;
+
+  keyid_from_pk (pk, keyid);
   uidnode = find_next_kbnode (keyblock, PKT_USER_ID);
-  
-  if (opt.verbose)
+
+  if (screener && screener (keyblock, screener_arg))
+    {
+      log_error (_("secret key %s: %s\n"), keystr_from_pk (pk),
+                 _("rejected by import screener"));
+      return 0;
+  }
+
+  if (opt.verbose && !for_migration)
     {
-      log_info ("sec  %4u%c/%s %s   ",
-                nbits_from_sk (sk),
-                pubkey_letter (sk->pubkey_algo),
-                keystr_from_sk (sk), datestr_from_sk (sk));
+      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 (es_stderr, uidnode->pkt->pkt.user_id->name,
+        print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name,
                            uidnode->pkt->pkt.user_id->len);
       log_printf ("\n");
     }
   stats->secret_read++;
-  
+
+  if ((options & IMPORT_NO_SECKEY))
+    {
+      if (!for_migration)
+        log_error (_("importing secret keys not allowed\n"));
+      return 0;
+    }
+
   if (!uidnode)
     {
-      log_error( _("key %s: no user ID\n"), keystr_from_sk(sk));
+      if (!for_migration)
+        log_error( _("key %s: no user ID\n"), keystr_from_pk (pk));
       return 0;
     }
-    
+
+  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;
+    }
+
   /* A quick check to not import keys with an invalid protection
      cipher algorithm (only checks the primary key, though).  */
-  if (sk->protect.algo > 110)
+  if (ski->algo > 110)
     {
-      log_error (_("key %s: secret key with invalid cipher %d"
-                   " - skipped\n"),keystr_from_sk(sk),sk->protect.algo);
+      if (!for_migration)
+        log_error (_("key %s: secret key with invalid cipher %d"
+                     " - skipped\n"), keystr_from_pk (pk), ski->algo);
       return 0;
     }
 
@@ -1439,69 +1621,62 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock,
       log_error (_("importing secret keys not allowed\n"));
       return 0;
     }
-#endif 
-    
-  clear_kbnode_flags( keyblock );
-  
-  have_seckey = have_secret_key_with_kid (keyid);
+#endif
 
-  if (!have_seckey && !(opt.import_options&IMPORT_MERGE_ONLY) )
-    {
-      /* We don't have this key, insert as a new key.  */
-      kbnode_t pub_keyblock;
+  clear_kbnode_flags (keyblock);
 
-      stats->secret_imported++;
-      if (is_status_enabled ()) 
-        print_import_ok (NULL, sk, 1|16);
+  nr_prev = stats->skipped_new_keys;
 
-      /* Make a public key out of this. */
-      pub_keyblock = sec_to_pub_keyblock (keyblock);
-      if (!pub_keyblock)
-        log_error ("oops: FIXME (bad error message)\n");
-      else
-        {
-          import_one (fname, pub_keyblock, stats,
-                      NULL, NULL, opt.import_options, 1);
-          /* Fixme: We should check for an invalid keyblock and
-             cancel the secret key import in this case.  */
-          release_kbnode (pub_keyblock);
-            
+  /* 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 ("oops: error getting public keyblock again\n");
+            log_error ("key %s: failed to re-lookup public key\n",
+                       keystr_from_pk (pk));
           else
             {
-              if (!transfer_secret_keys (ctrl, keyblock))
+             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_sk (sk));
-                  check_prefs (node);
+                              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);
             }
         }
     }
-  else if (have_seckey)
-    { 
-      /* We can't yet merge secret keys. - Well, with the new system
-         we can => FIXME  */
-      log_error( _("key %s: secret key part already available\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));
 
   return rc;
 }
@@ -1549,9 +1724,9 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     {
         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);
     }
@@ -1600,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 */
@@ -1647,11 +1822,11 @@ chk_self_sigs (const char *fname, kbnode_t keyblock,
   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)); ) 
+  for (n=keyblock; (n = find_next_kbnode (n, 0)); )
     {
       if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
@@ -1665,7 +1840,7 @@ chk_self_sigs (const char *fname, kbnode_t keyblock,
 
       if ( n->pkt->pkttype != PKT_SIGNATURE )
         continue;
-      
+
       sig = n->pkt->pkt.signature;
       if ( keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1] )
         {
@@ -1677,7 +1852,7 @@ chk_self_sigs (const char *fname, kbnode_t keyblock,
          import a fully-cached key which speeds things up. */
       if (!opt.no_sig_cache)
         check_key_signature (keyblock, n, NULL);
-      
+
       if ( IS_UID_SIG(sig) || IS_UID_REV(sig) )
         {
           KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
@@ -1687,16 +1862,16 @@ chk_self_sigs (const char *fname, kbnode_t keyblock,
                          keystr(keyid));
               return -1;  /* The complete keyblock is invalid.  */
             }
-          
+
           /* If it hasn't been marked valid yet, keep trying.  */
-          if (!(unode->flag&1)) 
+          if (!(unode->flag&1))
             {
               rc = check_key_signature (keyblock, n, NULL);
               if ( rc )
                 {
                   if ( opt.verbose )
                     {
-                      char *p = utf8_to_native 
+                      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 ?
@@ -1725,7 +1900,7 @@ chk_self_sigs (const char *fname, kbnode_t keyblock,
               n->flag |= 4;
             }
         }
-      else if ( IS_SUBKEY_SIG (sig) ) 
+      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
@@ -1754,19 +1929,19 @@ chk_self_sigs (const char *fname, kbnode_t keyblock,
               else
                 {
                   /* It's valid, so is it newer? */
-                  if (sig->timestamp >= bsdate) 
+                  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; 
+                          bsnode->flag |= 4;
                           if (opt.verbose)
                             log_info (_("key %s: removed multiple subkey"
                                         " binding\n"),keystr(keyid));
                         }
-                      
+
                       bsnode = n;
                       bsdate = sig->timestamp;
                     }
@@ -1811,12 +1986,12 @@ chk_self_sigs (const char *fname, kbnode_t keyblock,
                         {
                           /* Delete the last revocation sig since
                              this one is newer.  */
-                          rsnode->flag |= 4; 
+                          rsnode->flag |= 4;
                           if (opt.verbose)
                             log_info (_("key %s: removed multiple subkey"
                                         " revocation\n"),keystr(keyid));
                         }
-                      
+
                       rsnode = n;
                       rsdate = sig->timestamp;
                     }
@@ -2076,12 +2251,12 @@ collapse_uids( KBNODE *keyblock )
     {
       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;
@@ -2091,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)
     {
@@ -2146,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,
@@ -2312,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);
@@ -2331,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 );
@@ -2501,8 +2674,9 @@ 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
@@ -2534,233 +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;
-
-  log_debug ("FIXME: Do we need the stub at all?\n");
-  hd = keydb_new (); /* FIXME. */
-
-  /* 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);
-    if (an < MAX_FINGERPRINT_LEN)
-      memset (afp+an, 0, MAX_FINGERPRINT_LEN-an);
-    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;
-}