speedo: Improve speedo Makefile.
[gnupg.git] / g10 / import.c
index 63e64b9..ca35ce1 100644 (file)
@@ -1,12 +1,13 @@
 /* import.c - import a key into our key storage.
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- *               2005 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ *               2007, 2010, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2014  Werner Koch
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -15,9 +16,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <errno.h>
 #include <assert.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "errors.h"
+#include "status.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "trustdb.h"
 #include "main.h"
@@ -39,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;
@@ -60,14 +61,21 @@ struct stats_s {
 };
 
 
-static int import( IOBUF inp, const char* fname,
-                   struct stats_s *stats, 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 int options);
-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,
@@ -91,20 +99,28 @@ parse_import_options(char *str,unsigned int *options,int noisy)
 {
   struct parse_options import_opts[]=
     {
-      {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL},
-      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL},
-      {"fast-import",IMPORT_FAST,NULL},
-      {"convert-sk-to-pk",IMPORT_SK2PK,NULL},
-      {"merge-only",IMPORT_MERGE_ONLY,NULL},
-      {"import-clean",IMPORT_CLEAN_SIGS|IMPORT_CLEAN_UIDS,NULL},
-      {"import-clean-sigs",IMPORT_CLEAN_SIGS,NULL},
-      {"import-clean-uids",IMPORT_CLEAN_UIDS,NULL},
+      {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL,
+       N_("import signatures that are marked as local-only")},
+      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,
+       N_("repair damage from the pks keyserver during import")},
+      {"fast-import",IMPORT_FAST,NULL,
+       N_("do not update the trustdb after import")},
+      {"merge-only",IMPORT_MERGE_ONLY,NULL,
+       N_("only accept updates to existing keys")},
+      {"import-clean",IMPORT_CLEAN,NULL,
+       N_("remove unusable parts from key after import")},
+      {"import-minimal",IMPORT_MINIMAL|IMPORT_CLEAN,NULL,
+       N_("remove as much as possible from key after import")},
       /* Aliases for backward compatibility */
-      {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL},
-      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL},
+      {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL},
+      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL},
       /* dummy */
-      {"import-unusable-sigs",0,NULL},
-      {NULL,0,NULL}
+      {"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}
     };
 
   return parse_options(str,options,import_opts,noisy);
@@ -154,8 +170,10 @@ import_release_stats_handle (void *p)
  *
  */
 static int
-import_keys_internal( IOBUF inp, char **fnames, int nnames,
-                     void *stats_handle, unsigned int options )
+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,
+                      import_screener_t screener, void *screener_arg)
 {
     int i, rc = 0;
     struct stats_s *stats = stats_handle;
@@ -164,7 +182,8 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
         stats = import_new_stats_handle ();
 
     if (inp) {
-        rc = import( inp, "[stream]", stats, options);
+      rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options,
+                   screener, screener_arg);
     }
     else {
         if( !fnames && !nnames )
@@ -179,19 +198,22 @@ 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) );
-           else {
-               rc = import( inp2, fname, stats, options );
+               log_error(_("can't open '%s': %s\n"), fname, strerror(errno) );
+           else
+             {
+               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,
-                                     g10_errstr(rc) );
-           }
+                 log_error("import from '%s' failed: %s\n", fname,
+                           g10_errstr(rc) );
+             }
            if( !fname )
                break;
        }
@@ -208,45 +230,87 @@ 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, 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 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, 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 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;
+    KBNODE keyblock = NULL;  /* Need to initialize because gcc can't
+                                grasp the return semantics of
+                                read_block. */
     int rc = 0;
 
     getkey_disable_caches();
 
     if( !opt.no_armor ) { /* armored reading is not disabled */
-       armor_filter_context_t *afx = xmalloc_clear( sizeof *afx );
+       armor_filter_context_t *afx;
+
+        afx = new_armor_context ();
        afx->only_keyblocks = 1;
-       iobuf_push_filter2( inp, armor_filter, afx, 1 );
+       push_armor_filter (afx, inp);
+        release_armor_context (afx);
     }
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, stats, options );
-       else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
-                rc = import_secret_one( fname, keyblock, stats, options );
+          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 );
@@ -265,12 +329,64 @@ import( IOBUF inp, const char* fname,
     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)
 {
@@ -285,9 +401,9 @@ import_print_stats (void *hd)
            log_info(_("          w/o user IDs: %lu\n"), stats->no_user_id );
        if( stats->imported || stats->imported_rsa ) {
            log_info(_("              imported: %lu"), stats->imported );
-           if( stats->imported_rsa )
-               fprintf(stderr, "  (RSA: %lu)", stats->imported_rsa );
-           putc('\n', stderr);
+           if (stats->imported_rsa)
+              log_printf ("  (RSA: %lu)", stats->imported_rsa );
+           log_printf ("\n");
        }
        if( stats->unchanged )
            log_info(_("             unchanged: %lu\n"), stats->unchanged );
@@ -335,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 )
@@ -412,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
@@ -505,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);
@@ -551,18 +725,17 @@ print_import_check (PKT_public_key * pk, PKT_user_id * id)
 static void
 check_prefs_warning(PKT_public_key *pk)
 {
-  log_info(_("WARNING: key %s contains preferences for unavailable\n"),
-          keystr_from_pk(pk));
-  log_info(_("algorithms on these user IDs:\n"));
+  log_info(_("WARNING: key %s contains preferences for unavailable\n"
+             "algorithms on these user IDs:\n"), keystr_from_pk(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;
 
@@ -585,31 +758,37 @@ check_prefs(KBNODE keyblock)
 
              if(prefs->type==PREFTYPE_SYM)
                {
-                 if(check_cipher_algo(prefs->value))
+                 if (openpgp_cipher_test_algo (prefs->value))
                    {
-                     const char *algo=cipher_algo_to_string(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"
-                                " algorithm %s\n"),user,algo?algo:num);
+                                " algorithm %s\n"), user, algo);
                      problem=1;
                    }
                }
              else if(prefs->type==PREFTYPE_HASH)
                {
-                 if(check_digest_algo(prefs->value))
+                 if(openpgp_md_test_algo(prefs->value))
                    {
-                     const char *algo=digest_algo_to_string(prefs->value);
+                     const char *algo =
+                        (gcry_md_test_algo (prefs->value)
+                         ? num
+                         : gcry_md_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
                      log_info(_("         \"%s\": preference for digest"
-                                " algorithm %s\n"),user,algo?algo:num);
+                                " algorithm %s\n"), user, algo);
                      problem=1;
                    }
                }
              else if(prefs->type==PREFTYPE_ZIP)
                {
-                 if(check_compress_algo(prefs->value))
+                 if(check_compress_algo (prefs->value))
                    {
                      const char *algo=compress_algo_to_string(prefs->value);
                      if(!problem)
@@ -634,7 +813,7 @@ check_prefs(KBNODE keyblock)
 
       if(!opt.batch)
        {
-         STRLIST sl=NULL,locusr=NULL;
+         strlist_t sl=NULL,locusr=NULL;
          size_t fprlen=0;
          byte fpr[MAX_FINGERPRINT_LEN],*p;
          char username[(MAX_FINGERPRINT_LEN*2)+1];
@@ -648,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);
        }
@@ -658,29 +837,19 @@ check_prefs(KBNODE keyblock)
     }
 }
 
-static int
-clean_sigs_from_all_uids(KBNODE keyblock)
-{
-  KBNODE uidnode;
-  int deleted=0;
-
-  for(uidnode=keyblock->next;uidnode;uidnode=uidnode->next)
-    if(uidnode->pkt->pkttype==PKT_USER_ID)
-      deleted+=clean_sigs_from_uid(keyblock,uidnode,opt.verbose);
-
-  return deleted;
-}
-
-
 /****************
- * 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 int options )
+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;
@@ -690,7 +859,9 @@ import_one( const char *fname, KBNODE keyblock,
     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 );
@@ -698,28 +869,38 @@ import_one( const char *fname, KBNODE keyblock,
        BUG();
 
     pk = node->pkt->pkt.public_key;
+
     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 );
-       putc('\n', stderr);
+       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);
@@ -731,15 +912,14 @@ import_one( const char *fname, KBNODE keyblock,
             return 0;
     }
 
+    collapse_uids(&keyblock);
+
     /* Clean the key that we're about to import, to cut down on things
        that we have to clean later.  This has no practical impact on
        the end result, but does result in less logging which might
        confuse the user. */
-    if(options&IMPORT_CLEAN_SIGS)
-      clean_sigs_from_all_uids(keyblock);
-
-    if(options&IMPORT_CLEAN_UIDS)
-      clean_uids_from_key(keyblock,opt.verbose);
+    if(options&IMPORT_CLEAN)
+      clean_key(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL);
 
     clear_kbnode_flags( keyblock );
 
@@ -753,7 +933,7 @@ import_one( const char *fname, KBNODE keyblock,
        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) )
          {
@@ -766,9 +946,11 @@ import_one( const char *fname, KBNODE keyblock,
          }
 
     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;
     }
@@ -778,33 +960,32 @@ import_one( const char *fname, KBNODE keyblock,
     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) );
-
-       collapse_uids(&keyblock);
+           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
          {
@@ -820,7 +1001,7 @@ import_one( const char *fname, KBNODE keyblock,
         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"),
@@ -832,7 +1013,7 @@ import_one( const char *fname, KBNODE keyblock,
            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 ) )
@@ -847,18 +1028,19 @@ import_one( const char *fname, KBNODE keyblock,
         * 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);
         }
@@ -878,11 +1060,15 @@ import_one( const char *fname, KBNODE keyblock,
            goto leave;
          }
 
-       collapse_uids( &keyblock );
+        /* 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 )
@@ -891,24 +1077,22 @@ import_one( const char *fname, KBNODE keyblock,
            goto leave;
          }
 
-       if(options&IMPORT_CLEAN_SIGS)
-         n_sigs_cleaned=clean_sigs_from_all_uids(keyblock_orig);
-
-        if(options&IMPORT_CLEAN_UIDS)
-         n_uids_cleaned=clean_uids_from_key(keyblock_orig,opt.verbose);
+       if(options&IMPORT_CLEAN)
+         clean_key(keyblock_orig,opt.verbose,options&IMPORT_MINIMAL,
+                   &n_uids_cleaned,&n_sigs_cleaned);
 
        if( n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) {
            mod_key = 1;
            /* keyblock_orig has been updated; write */
            rc = keydb_update_keyblock (hd, keyblock_orig);
             if (rc)
-               log_error (_("error writing keyring `%s': %s\n"),
+               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 )
@@ -950,16 +1134,16 @@ import_one( const char *fname, KBNODE keyblock,
            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);
@@ -973,22 +1157,49 @@ import_one( const char *fname, KBNODE keyblock,
     }
 
   leave:
+    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
+          fingerprint in certain keyserver-related cases (a keyserver
+          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
+          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);
+            /* 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;
+         }
+      }
 
     /* 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)
+    if (mod_key)
       {
-       revocation_present(keyblock_orig);
-       if(seckey_available(keyid)==0)
-         check_prefs(keyblock_orig);
+       revocation_present (ctrl, keyblock_orig);
+       if (!from_sk && have_secret_key_with_kid (keyid))
+         check_prefs (ctrl, keyblock_orig);
       }
-    else if(new_key)
+    else if (new_key)
       {
-       revocation_present(keyblock);
-       if(seckey_available(keyid)==0)
-         check_prefs(keyblock);
+       revocation_present (ctrl, keyblock);
+       if (!from_sk && have_secret_key_with_kid (keyid))
+         check_prefs (ctrl, keyblock);
       }
 
     release_kbnode( keyblock_orig );
@@ -997,66 +1208,323 @@ import_one( const char *fname, KBNODE keyblock,
     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;
+}
 
-         pubnode=new_kbnode(pkt);
+
+/* 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);
        }
       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;
@@ -1069,126 +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 );
-       putc('\n', stderr);
-      }
-    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,opt.import_options);
-               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;
 }
 
 
@@ -1204,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 );
@@ -1228,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);
     }
@@ -1283,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 */
@@ -1312,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
@@ -1322,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;
@@ -1503,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;
@@ -1545,14 +2064,14 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
            else
              subkey_seen = 1;
        }
-       else ifnode->pkt->pkttype == PKT_SIGNATURE
-                && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo)
-                && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
+       else if (node->pkt->pkttype == PKT_SIGNATURE
+                && openpgp_pk_test_algo (node->pkt->pkt.signature->pubkey_algo)
+               && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
            delete_kbnode( node ); /* build_packet() can't handle this */
        else if( node->pkt->pkttype == PKT_SIGNATURE &&
                 !node->pkt->pkt.signature->flags.exportable &&
                 !(options&IMPORT_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
@@ -1628,100 +2147,129 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
  * It may happen that the imported keyblock has duplicated user IDs.
  * We check this here and collapse those user IDs together with their
  * sigs into one.
- * Returns: True if the keyblock hash changed.
+ * Returns: True if the keyblock has changed.
  */
 int
 collapse_uids( KBNODE *keyblock )
 {
-    KBNODE n, n2;
-    int in_uid;
-    int any=0;
+  KBNODE uid1;
+  int any=0;
+
+  for(uid1=*keyblock;uid1;uid1=uid1->next)
+    {
+      KBNODE uid2;
+
+      if(is_deleted_kbnode(uid1))
+       continue;
+
+      if(uid1->pkt->pkttype!=PKT_USER_ID)
+       continue;
 
-  restart:
-    for( n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype != PKT_USER_ID )
+      for(uid2=uid1->next;uid2;uid2=uid2->next)
+       {
+         if(is_deleted_kbnode(uid2))
            continue;
-       for( n2 = n->next; n2; n2 = n2->next ) {
-           if( n2->pkt->pkttype == PKT_USER_ID
-               && !cmp_user_ids( n->pkt->pkt.user_id,
-                                 n2->pkt->pkt.user_id ) ) {
-               /* found a duplicate */
-               any = 1;
-               if( !n2->next
-                   || n2->next->pkt->pkttype == PKT_USER_ID
-                   || n2->next->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                   || n2->next->pkt->pkttype == PKT_SECRET_SUBKEY  ) {
-                   /* no more signatures: delete the user ID
-                    * and start over */
-                   remove_kbnode( keyblock, n2 );
-               }
-               else {
-                   /* The simple approach: Move one signature and
-                    * then start over to delete the next one :-( */
-                   move_kbnode( keyblock, n2->next, n->next );
+
+         if(uid2->pkt->pkttype!=PKT_USER_ID)
+           continue;
+
+         if(cmp_user_ids(uid1->pkt->pkt.user_id,
+                         uid2->pkt->pkt.user_id)==0)
+           {
+             /* We have a duplicated uid */
+             KBNODE sig1,last;
+
+             any=1;
+
+             /* Now take uid2's signatures, and attach them to
+                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)
+                   break;
                }
-               goto restart;
-           }
-       }
-    }
-    if( !any )
-       return 0;
 
-  restart_sig:
-    /* now we may have duplicate signatures on one user ID: fix this */
-    for( in_uid = 0, n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype == PKT_USER_ID )
-           in_uid = 1;
-       else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                || n->pkt->pkttype == PKT_SECRET_SUBKEY )
-           in_uid = 0;
-       else if( in_uid ) {
-           n2 = n;
-           do {
-               KBNODE ncmp = NULL;
-               for( ; n2; n2 = n2->next ) {
-                   if(    n2->pkt->pkttype == PKT_USER_ID
-                       || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                       || n2->pkt->pkttype == PKT_SECRET_SUBKEY )
+             /* Snip out uid2 */
+             (find_prev_kbnode(*keyblock,uid2,0))->next=last->next;
+
+             /* Now put uid2 in place as part of uid1 */
+             last->next=uid1->next;
+             uid1->next=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)
+                   break;
+
+                 if(sig1->pkt->pkttype!=PKT_SIGNATURE)
+                   continue;
+
+                 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)
                        break;
-                   if( n2->pkt->pkttype != PKT_SIGNATURE )
-                       ;
-                   else if( !ncmp )
-                       ncmp = n2;
-                   else if( !cmp_signatures( ncmp->pkt->pkt.signature,
-                                               n2->pkt->pkt.signature )) {
-                       remove_kbnode( keyblock, n2 );
-                       goto restart_sig;
+
+                     if(sig2->pkt->pkttype!=PKT_SIGNATURE)
+                       continue;
+
+                     if(cmp_signatures(sig1->pkt->pkt.signature,
+                                       sig2->pkt->pkt.signature)==0)
+                       {
+                         /* We have a match, so delete the second
+                            signature */
+                         delete_kbnode(sig2);
+                         sig2=last;
+                       }
                    }
                }
-               n2 = ncmp? ncmp->next : NULL;
-           } while( n2 );
+           }
        }
     }
 
-    if(!opt.quiet)
-      {
-       const char *key="???";
+  commit_kbnode(keyblock);
 
-       if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
-         key=keystr_from_pk(n->pkt->pkt.public_key);
-       else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
-         key=keystr_from_sk(n->pkt->pkt.secret_key);
+  if(any && !opt.quiet)
+    {
+      const char *key="???";
 
-       log_info(_("key %s: duplicated user ID detected - merged\n"),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);
+    }
 
-    return 1;
+  return any;
 }
 
 /* Check for a 0x20 revocation from a revocation key that is not
    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)
     {
@@ -1773,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,
@@ -1939,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);
@@ -1958,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 );
@@ -1984,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 */
@@ -2036,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 );
 
@@ -2071,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 );
 
@@ -2119,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 );
 
@@ -2149,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;
-}
-