* gpg.c (print_mds), armor.c (armor_filter, parse_hash_header): Add
[gnupg.git] / g10 / import.c
index 368ea8b..3b7fa5e 100644 (file)
@@ -1,5 +1,6 @@
-/* import.c
- *     Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+/* import.c - import a key into our key storage.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include "trustdb.h"
 #include "main.h"
 #include "i18n.h"
+#include "ttyio.h"
 #include "status.h"
+#include "keyserver-internal.h"
 
-
-static struct {
+struct stats_s {
+    ulong count;
     ulong no_user_id;
     ulong imported;
     ulong imported_rsa;
@@ -49,18 +53,28 @@ static struct {
     ulong secret_read;
     ulong secret_imported;
     ulong secret_dups;
-} stats;
+    ulong skipped_new_keys;
+    ulong not_imported;
+    ulong n_sigs_cleaned;
+    ulong n_uids_cleaned;
+};
 
 
-static int import( IOBUF inp, int fast, const char* fname );
+static int import( IOBUF inp, const char* fname,struct stats_s *stats,
+                  unsigned char **fpr,size_t *fpr_len,unsigned int options );
 static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
-static int import_one( const char *fname, KBNODE keyblock, int fast );
-static int import_secret_one( const char *fname, KBNODE keyblock );
-static int import_revoke_cert( const char *fname, KBNODE node );
+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);
+static int import_secret_one( const char *fname, KBNODE keyblock,
+                              struct stats_s *stats, unsigned int options);
+static int import_revoke_cert( const char *fname, KBNODE node,
+                               struct stats_s *stats);
 static int chk_self_sigs( const char *fname, KBNODE keyblock,
-                         PKT_public_key *pk, u32 *keyid );
-static void mark_non_selfsigned_uids_valid( KBNODE keyblock, u32 *kid );
-static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid );
+                         PKT_public_key *pk, u32 *keyid, int *non_self );
+static int delete_inv_parts( const char *fname, KBNODE keyblock,
+                            u32 *keyid, unsigned int options );
 static int merge_blocks( const char *fname, KBNODE keyblock_orig,
                         KBNODE keyblock, u32 *keyid,
                         int *n_uids, int *n_sigs, int *n_subk );
@@ -73,6 +87,49 @@ static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
 static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
                             const char *fname, u32 *keyid );
 
+int
+parse_import_options(char *str,unsigned int *options,int noisy)
+{
+  struct parse_options import_opts[]=
+    {
+      {"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")},
+      {"convert-sk-to-pk",IMPORT_SK2PK,NULL,
+       N_("create a public key when importing a secret key")},
+      {"merge-only",IMPORT_MERGE_ONLY,NULL,
+       N_("only accept updates to existing keys")},
+      {"import-clean",IMPORT_CLEAN,NULL,
+       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,NULL},
+      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL},
+      /* dummy */
+      {"import-unusable-sigs",0,NULL,NULL},
+      {"import-clean-sigs",0,NULL,NULL},
+      {"import-clean-uids",0,NULL,NULL},
+      {NULL,0,NULL,NULL}
+    };
+
+  return parse_options(str,options,import_opts,noisy);
+}
+
+void *
+import_new_stats_handle (void)
+{
+    return xmalloc_clear ( sizeof (struct stats_s) );
+}
+
+void
+import_release_stats_handle (void *p)
+{
+    xfree (p);
+}
 
 /****************
  * Import the public keys from the given filename. Input may be armored.
@@ -105,121 +162,188 @@ static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
  *  Key revocation certificates have special handling.
  *
  */
-int
-import_keys( const char *fname, int fast )
+static int
+import_keys_internal( IOBUF inp, char **fnames, int nnames,
+                     void *stats_handle, unsigned char **fpr, size_t *fpr_len,
+                     unsigned int options )
 {
-    IOBUF inp = NULL;
-    int rc;
+    int i, rc = 0;
+    struct stats_s *stats = stats_handle;
+
+    if (!stats)
+        stats = import_new_stats_handle ();
 
-    inp = iobuf_open(fname);
-    if( !fname )
-       fname = "[stdin]";
-    if( !inp ) {
-       log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
-       return G10ERR_OPEN_FILE;
+    if (inp) {
+        rc = import( inp, "[stream]", stats, fpr, fpr_len, options);
+    }
+    else {
+        if( !fnames && !nnames )
+           nnames = 1;  /* Ohh what a ugly hack to jump into the loop */
+
+       for(i=0; i < nnames; i++ ) {
+           const char *fname = fnames? fnames[i] : NULL;
+           IOBUF inp2 = iobuf_open(fname);
+           if( !fname )
+               fname = "[stdin]";
+            if (inp2 && is_secured_file (iobuf_get_fd (inp2)))
+              {
+                iobuf_close (inp2);
+                inp2 = NULL;
+                errno = EPERM;
+              }
+           if( !inp2 )
+               log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+           else
+             {
+               rc = import( inp2, fname, stats, fpr, fpr_len, options );
+               iobuf_close(inp2);
+                /* Must invalidate that ugly cache to actually close it. */
+                iobuf_ioctl (NULL, 2, 0, (char*)fname);
+               if( rc )
+                 log_error("import from `%s' failed: %s\n", fname,
+                           g10_errstr(rc) );
+             }
+           if( !fname )
+               break;
+       }
+    }
+    if (!stats_handle) {
+        import_print_stats (stats);
+        import_release_stats_handle (stats);
     }
 
-    rc = import( inp, fast, fname );
+    /* If no fast import and the trustdb is dirty (i.e. we added a key
+       or userID that had something other than a selfsig, a signature
+       that was other than a selfsig, or any revocation), then
+       update/check the trustdb if the user specified by setting
+       interactive or by not setting no-auto-check-trustdb */
+
+    if(!(options&IMPORT_FAST))
+      trustdb_check_or_update();
 
-    iobuf_close(inp);
     return rc;
 }
 
+void
+import_keys( char **fnames, int nnames,
+            void *stats_handle, unsigned int options )
+{
+  import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options);
+}
+
 int
-import_keys_stream( IOBUF inp, int fast )
+import_keys_stream( IOBUF inp, void *stats_handle,
+                   unsigned char **fpr, size_t *fpr_len,unsigned int options )
 {
-    return import( inp, fast, "[stream]" );
+  return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options);
 }
 
 static int
-import( IOBUF inp, int fast, const char* fname )
+import( IOBUF inp, const char* fname,struct stats_s *stats,
+       unsigned char **fpr,size_t *fpr_len,unsigned int options )
 {
     PACKET *pending_pkt = NULL;
     KBNODE keyblock;
     int rc = 0;
-    ulong count=0;
-
-    /* fixme: don't use static variables */
-    memset( &stats, 0, sizeof( stats ) );
 
     getkey_disable_caches();
 
     if( !opt.no_armor ) { /* armored reading is not disabled */
-       armor_filter_context_t *afx = m_alloc_clear( sizeof *afx );
+       armor_filter_context_t *afx = xmalloc_clear( sizeof *afx );
        afx->only_keyblocks = 1;
        iobuf_push_filter2( inp, armor_filter, afx, 1 );
     }
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, fast );
-       else if( keyblock->pkt->pkttype == PKT_SECRET_KEY )
-           rc = import_secret_one( fname, keyblock );
+           rc = import_one( fname, keyblock, stats, fpr, fpr_len, options );
+       else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
+                rc = import_secret_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SIGNATURE
                 && keyblock->pkt->pkt.signature->sig_class == 0x20 )
-           rc = import_revoke_cert( fname, keyblock );
+           rc = import_revoke_cert( fname, keyblock, stats );
        else {
            log_info( _("skipping block of type %d\n"),
                                            keyblock->pkt->pkttype );
        }
        release_kbnode(keyblock);
+        /* fixme: we should increment the not imported counter but this
+           does only make sense if we keep on going despite of errors. */
        if( rc )
            break;
-       if( !(++count % 100) && !opt.quiet )
-           log_info(_("%lu keys so far processed\n"), count );
+       if( !(++stats->count % 100) && !opt.quiet )
+           log_info(_("%lu keys processed so far\n"), stats->count );
     }
     if( rc == -1 )
        rc = 0;
     else if( rc && rc != G10ERR_INV_KEYRING )
        log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc));
 
+    return rc;
+}
+
+
+void
+import_print_stats (void *hd)
+{
+    struct stats_s *stats = hd;
+
     if( !opt.quiet ) {
-       log_info(_("Total number processed: %lu\n"), count );
-       if( stats.no_user_id )
-           log_info(_("          w/o user IDs: %lu\n"), stats.no_user_id );
-       if( stats.imported || stats.imported_rsa ) {
-           log_info(_("              imported: %lu"), stats.imported );
-           if( stats.imported_rsa )
-               fprintf(stderr, "  (RSA: %lu)", stats.imported_rsa );
+       log_info(_("Total number processed: %lu\n"), stats->count );
+       if( stats->skipped_new_keys )
+           log_info(_("      skipped new keys: %lu\n"),
+                                               stats->skipped_new_keys );
+       if( stats->no_user_id )
+           log_info(_("          w/o user IDs: %lu\n"), stats->no_user_id );
+       if( stats->imported || stats->imported_rsa ) {
+           log_info(_("              imported: %lu"), stats->imported );
+           if( stats->imported_rsa )
+               fprintf(stderr, "  (RSA: %lu)", stats->imported_rsa );
            putc('\n', stderr);
        }
-       if( stats.unchanged )
-           log_info(_("             unchanged: %lu\n"), stats.unchanged );
-       if( stats.n_uids )
-           log_info(_("          new user IDs: %lu\n"), stats.n_uids );
-       if( stats.n_subk )
-           log_info(_("           new subkeys: %lu\n"), stats.n_subk );
-       if( stats.n_sigs )
-           log_info(_("        new signatures: %lu\n"), stats.n_sigs );
-       if( stats.n_revoc )
-           log_info(_("   new key revocations: %lu\n"), stats.n_revoc );
-       if( stats.secret_read )
-           log_info(_("      secret keys read: %lu\n"), stats.secret_read );
-       if( stats.secret_imported )
-           log_info(_("  secret keys imported: %lu\n"), stats.secret_imported );
-       if( stats.secret_dups )
-           log_info(_(" secret keys unchanged: %lu\n"), stats.secret_dups );
+       if( stats->unchanged )
+           log_info(_("             unchanged: %lu\n"), stats->unchanged );
+       if( stats->n_uids )
+           log_info(_("          new user IDs: %lu\n"), stats->n_uids );
+       if( stats->n_subk )
+           log_info(_("           new subkeys: %lu\n"), stats->n_subk );
+       if( stats->n_sigs )
+           log_info(_("        new signatures: %lu\n"), stats->n_sigs );
+       if( stats->n_revoc )
+           log_info(_("   new key revocations: %lu\n"), stats->n_revoc );
+       if( stats->secret_read )
+           log_info(_("      secret keys read: %lu\n"), stats->secret_read );
+       if( stats->secret_imported )
+           log_info(_("  secret keys imported: %lu\n"), stats->secret_imported );
+       if( stats->secret_dups )
+           log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups );
+       if( stats->not_imported )
+           log_info(_("          not imported: %lu\n"), stats->not_imported );
+       if( stats->n_sigs_cleaned)
+           log_info(_("    signatures cleaned: %lu\n"),stats->n_sigs_cleaned);
+       if( stats->n_uids_cleaned)
+           log_info(_("      user IDs cleaned: %lu\n"),stats->n_uids_cleaned);
     }
 
     if( is_status_enabled() ) {
-       char buf[12*16];
-       sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
-               count,
-               stats.no_user_id,
-               stats.imported,
-               stats.imported_rsa,
-               stats.unchanged,
-               stats.n_uids,
-               stats.n_subk,
-               stats.n_sigs,
-               stats.n_revoc,
-               stats.secret_read,
-               stats.secret_imported,
-               stats.secret_dups);
+       char buf[14*20];
+       sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+               stats->count,
+               stats->no_user_id,
+               stats->imported,
+               stats->imported_rsa,
+               stats->unchanged,
+               stats->n_uids,
+               stats->n_subk,
+               stats->n_sigs,
+               stats->n_revoc,
+               stats->secret_read,
+               stats->secret_imported,
+               stats->secret_dups,
+               stats->skipped_new_keys,
+                stats->not_imported );
        write_status_text( STATUS_IMPORT_RES, buf );
     }
-
-    return rc;
 }
 
 
@@ -244,7 +368,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     }
     else
        in_cert = 0;
-    pkt = m_alloc( sizeof *pkt );
+    pkt = xmalloc( sizeof *pkt );
     init_packet(pkt);
     while( (rc=parse_packet(a, pkt)) != -1 ) {
        if( rc ) {  /* ignore errors */
@@ -270,21 +394,26 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
        /* make a linked list of all packets */
        switch( pkt->pkttype ) {
          case PKT_COMPRESSED:
-           if( pkt->pkt.compressed->algorithm < 1
-               || pkt->pkt.compressed->algorithm > 2 ) {
+           if(check_compress_algo(pkt->pkt.compressed->algorithm))
+             {
                rc = G10ERR_COMPR_ALGO;
                goto ready;
-           }
-           {
-               compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx );
-               cfx->algo = pkt->pkt.compressed->algorithm;
+             }
+           else
+             {
+               compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx );
                pkt->pkt.compressed->buf = NULL;
-               iobuf_push_filter2( a, compress_filter, cfx, 1 );
-           }
+               push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1);
+             }
            free_packet( pkt );
            init_packet(pkt);
            break;
 
+          case PKT_RING_TRUST:
+            /* skip those packets */
+           free_packet( pkt );
+           init_packet(pkt);
+            break;
 
          case PKT_PUBLIC_KEY:
          case PKT_SECRET_KEY:
@@ -300,7 +429,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
                    root = new_kbnode( pkt );
                else
                    add_kbnode( root, new_kbnode( pkt ) );
-               pkt = m_alloc( sizeof *pkt );
+               pkt = xmalloc( sizeof *pkt );
            }
            init_packet(pkt);
            break;
@@ -315,10 +444,230 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     else
        *ret_root = root;
     free_packet( pkt );
-    m_free( pkt );
+    xfree( pkt );
     return rc;
 }
 
+/* Walk through the subkeys on a pk to find if we have the PKS
+   disease: multiple subkeys with their binding sigs stripped, and the
+   sig for the first subkey placed after the last subkey.  That is,
+   instead of "pk uid sig sub1 bind1 sub2 bind2 sub3 bind3" we have
+   "pk uid sig sub1 sub2 sub3 bind1".  We can't do anything about sub2
+   and sub3, as they are already lost, but we can try and rescue sub1
+   by reordering the keyblock so that it reads "pk uid sig sub1 bind1
+   sub2 sub3".  Returns TRUE if the keyblock was modified. */
+
+static int
+fix_pks_corruption(KBNODE keyblock)
+{
+  int changed=0,keycount=0;
+  KBNODE node,last=NULL,sknode=NULL;
+
+  /* First determine if we have the problem at all.  Look for 2 or
+     more subkeys in a row, followed by a single binding sig. */
+  for(node=keyblock;node;last=node,node=node->next)
+    {
+      if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+       {
+         keycount++;
+         if(!sknode)
+           sknode=node;
+       }
+      else if(node->pkt->pkttype==PKT_SIGNATURE &&
+             node->pkt->pkt.signature->sig_class==0x18 &&
+             keycount>=2 && node->next==NULL)
+       {
+         /* We might have the problem, as this key has two subkeys in
+            a row without any intervening packets. */
+
+         /* Sanity check */
+         if(last==NULL)
+           break;
+
+         /* Temporarily attach node to sknode. */
+         node->next=sknode->next;
+         sknode->next=node;
+         last->next=NULL;
+
+         /* Note we aren't checking whether this binding sig is a
+            selfsig.  This is not necessary here as the subkey and
+            binding sig will be rejected later if that is the
+            case. */
+         if(check_key_signature(keyblock,node,NULL))
+           {
+             /* Not a match, so undo the changes. */
+             sknode->next=node->next;
+             last->next=node;
+             node->next=NULL;
+             break;
+           }
+         else
+           {
+             sknode->flag |= 1; /* Mark it good so we don't need to
+                                    check it again */
+             changed=1;
+             break;
+           }
+       }
+      else
+       keycount=0;
+    }
+
+  return changed;
+}
+
+
+static void
+print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
+{
+  byte array[MAX_FINGERPRINT_LEN], *s;
+  char buf[MAX_FINGERPRINT_LEN*2+30], *p;
+  size_t i, n;
+
+  sprintf (buf, "%u ", reason);
+  p = buf + strlen (buf);
+
+  if (pk)
+    fingerprint_from_pk (pk, array, &n);
+  else
+    fingerprint_from_sk (sk, array, &n);
+  s = array;
+  for (i=0; i < n ; i++, s++, p += 2)
+    sprintf (p, "%02X", *s);
+
+  write_status_text (STATUS_IMPORT_OK, buf);
+}
+
+static void
+print_import_check (PKT_public_key * pk, PKT_user_id * id)
+{
+    char * buf;
+    byte fpr[24];
+    u32 keyid[2];
+    size_t i, pos = 0, n;
+
+    buf = xmalloc (17+41+id->len+32);
+    keyid_from_pk (pk, keyid);
+    sprintf (buf, "%08X%08X ", keyid[0], keyid[1]);
+    pos = 17;
+    fingerprint_from_pk (pk, fpr, &n);
+    for (i = 0; i < n; i++, pos += 2)
+        sprintf (buf+pos, "%02X", fpr[i]);
+    strcat (buf, " ");
+    pos += 1;
+    strcat (buf, id->name);
+    write_status_text (STATUS_IMPORT_CHECK, buf);
+    xfree (buf);
+}
+
+static void
+check_prefs_warning(PKT_public_key *pk)
+{
+  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)
+{
+  KBNODE node;
+  PKT_public_key *pk;
+  int problem=0;
+  
+  merge_keys_and_selfsig(keyblock);
+  pk=keyblock->pkt->pkt.public_key;
+
+  for(node=keyblock;node;node=node->next)
+    {
+      if(node->pkt->pkttype==PKT_USER_ID
+        && node->pkt->pkt.user_id->created
+        && node->pkt->pkt.user_id->prefs)
+       {
+         PKT_user_id *uid=node->pkt->pkt.user_id;
+         prefitem_t *prefs=uid->prefs;
+         char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+
+         for(;prefs->type;prefs++)
+           {
+             char num[10]; /* prefs->value is a byte, so we're over
+                              safe here */
+
+             sprintf(num,"%u",prefs->value);
+
+             if(prefs->type==PREFTYPE_SYM)
+               {
+                 if(check_cipher_algo(prefs->value))
+                   {
+                     const char *algo=cipher_algo_to_string(prefs->value);
+                     if(!problem)
+                       check_prefs_warning(pk);
+                     log_info(_("         \"%s\": preference for cipher"
+                                " algorithm %s\n"),user,algo?algo:num);
+                     problem=1;
+                   }
+               }
+             else if(prefs->type==PREFTYPE_HASH)
+               {
+                 if(check_digest_algo(prefs->value))
+                   {
+                     const char *algo=digest_algo_to_string(prefs->value);
+                     if(!problem)
+                       check_prefs_warning(pk);
+                     log_info(_("         \"%s\": preference for digest"
+                                " algorithm %s\n"),user,algo?algo:num);
+                     problem=1;
+                   }
+               }
+             else if(prefs->type==PREFTYPE_ZIP)
+               {
+                 if(check_compress_algo(prefs->value))
+                   {
+                     const char *algo=compress_algo_to_string(prefs->value);
+                     if(!problem)
+                       check_prefs_warning(pk);
+                     log_info(_("         \"%s\": preference for compression"
+                                " algorithm %s\n"),user,algo?algo:num);
+                     problem=1;
+                   }
+               }
+           }
+
+         xfree(user);
+       }
+    }
+
+  if(problem)
+    {
+      log_info(_("it is strongly suggested that you update"
+                " your preferences and\n"));
+      log_info(_("re-distribute this key to avoid potential algorithm"
+                " mismatch problems\n"));
+
+      if(!opt.batch)
+       {
+         STRLIST sl=NULL,locusr=NULL;
+         size_t fprlen=0;
+         byte fpr[MAX_FINGERPRINT_LEN],*p;
+         char username[(MAX_FINGERPRINT_LEN*2)+1];
+         unsigned int i;
+
+         p=fingerprint_from_pk(pk,fpr,&fprlen);
+         for(i=0;i<fprlen;i++,p++)
+           sprintf(username+2*i,"%02X",*p);
+         add_to_strlist(&locusr,username);
+
+         append_to_strlist(&sl,"updpref");
+         append_to_strlist(&sl,"save");
+
+         keyedit_menu( username, locusr, sl, 1, 1 );
+         free_strlist(sl);
+         free_strlist(locusr);
+       }
+      else if(!opt.quiet)
+       log_info(_("you can update your preferences with:"
+                  " gpg --edit-key %s updpref save\n"),keystr_from_pk(pk));
+    }
+}
 
 /****************
  * Try to import one keyblock. Return an error only in serious cases, but
@@ -327,17 +676,18 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
  * which called g10.
  */
 static int
-import_one( const char *fname, KBNODE keyblock, int fast )
+import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
+           unsigned char **fpr,size_t *fpr_len,unsigned int options )
 {
     PKT_public_key *pk;
     PKT_public_key *pk_orig;
     KBNODE node, uidnode;
     KBNODE keyblock_orig = NULL;
-    KBPOS kbpos;
     u32 keyid[2];
     int rc = 0;
     int new_key = 0;
     int mod_key = 0;
+    int non_self = 0;
 
     /* get the key and print some info about it */
     node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
@@ -345,188 +695,380 @@ import_one( const char *fname, KBNODE keyblock, int fast )
        BUG();
 
     pk = node->pkt->pkt.public_key;
+
+    if(fpr)
+      *fpr=fingerprint_from_pk(pk,NULL,fpr_len);
+
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if( opt.verbose ) {
-       log_info( "pub  %4u%c/%08lX %s   ",
+    if( opt.verbose && !opt.interactive )
+      {
+       log_info( "pub  %4u%c/%s %s  ",
                  nbits_from_pk( pk ),
                  pubkey_letter( pk->pubkey_algo ),
-                 (ulong)keyid[1], datestr_from_pk(pk) );
+                 keystr_from_pk(pk), datestr_from_pk(pk) );
        if( uidnode )
-           print_string( stderr, uidnode->pkt->pkt.user_id->name,
-                                 uidnode->pkt->pkt.user_id->len, 0 );
+         print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+                            uidnode->pkt->pkt.user_id->len );
        putc('\n', stderr);
-    }
-    if( !uidnode ) {
-       log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]);
+      }
+
+    if( !uidnode )
+      {
+       log_error( _("key %s: no user ID\n"), keystr_from_pk(pk));
        return 0;
+      }
+    
+    if (opt.interactive) {
+        if(is_status_enabled())
+         print_import_check (pk, uidnode->pkt->pkt.user_id);
+       merge_keys_and_selfsig (keyblock);
+        tty_printf ("\n");
+        show_basic_key_info (keyblock);
+        tty_printf ("\n");
+        if (!cpr_get_answer_is_yes ("import.okay",
+                                    "Do you want to import this key? (y/N) "))
+            return 0;
     }
 
+    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)
+      clean_key(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL);
+
     clear_kbnode_flags( keyblock );
-    rc = chk_self_sigs( fname, keyblock , pk, keyid );
+
+    if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock)
+       && opt.verbose)
+      log_info(_("key %s: PKS subkey corruption repaired\n"),
+              keystr_from_pk(pk));
+
+    rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self );
     if( rc )
        return rc== -1? 0:rc;
 
+    /* If we allow such a thing, mark unsigned uids as valid */
     if( opt.allow_non_selfsigned_uid )
-       mark_non_selfsigned_uids_valid( keyblock, keyid );
-
-    if( !delete_inv_parts( fname, keyblock, keyid ) ) {
-       if( !opt.quiet ) {
-           log_info( _("key %08lX: no valid user IDs\n"),
-                                                       (ulong)keyid[1]);
-           log_info(_("this may be caused by a missing self-signature\n"));
-       }
-       stats.no_user_id++;
+      for( node=keyblock; node; node = node->next )
+       if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) )
+         {
+           char *user=utf8_to_native(node->pkt->pkt.user_id->name,
+                                     node->pkt->pkt.user_id->len,0);
+           node->flag |= 1;
+           log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"),
+                     keystr_from_pk(pk),user);
+           xfree(user);
+         }
+
+    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"));
+       stats->no_user_id++;
        return 0;
     }
 
-
     /* do we have this key already in one of our pubrings ? */
-    pk_orig = m_alloc_clear( sizeof *pk_orig );
-    rc = get_pubkey( pk_orig, keyid );
-    if( rc && rc != G10ERR_NO_PUBKEY ) {
-       log_error( _("key %08lX: public key not found: %s\n"),
-                               (ulong)keyid[1], g10_errstr(rc));
-    }
+    pk_orig = xmalloc_clear( sizeof *pk_orig );
+    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));
+      }
+    else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) )
+      {
+       if( opt.verbose )
+         log_info( _("key %s: new key - skipped\n"), keystr(keyid));
+       rc = 0;
+       stats->skipped_new_keys++;
+      }
     else if( rc ) { /* insert this key */
-       /* get default resource */
-       if( get_keyblock_handle( NULL, 0, &kbpos ) ) {
-           log_error(_("no default public keyring\n"));
+        KEYDB_HANDLE hd = keydb_new (0);
+
+        rc = keydb_locate_writable (hd, NULL);
+       if (rc) {
+           log_error (_("no writable keyring found: %s\n"), g10_errstr (rc));
+            keydb_release (hd);
            return G10ERR_GENERAL;
        }
        if( opt.verbose > 1 )
-           log_info( _("writing to `%s'\n"),
-                               keyblock_resource_name(&kbpos) );
-       if( (rc=lock_keyblock( &kbpos )) )
-          log_error(_("can't lock keyring `%s': %s\n"),
-                      keyblock_resource_name(&kbpos), g10_errstr(rc) );
-       else if( (rc=insert_keyblock( &kbpos, keyblock )) )
-          log_error( _("error writing keyring `%s': %s\n"),
-                      keyblock_resource_name(&kbpos), g10_errstr(rc) );
-       unlock_keyblock( &kbpos );
+           log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
+
+       rc = keydb_insert_keyblock (hd, keyblock );
+        if (rc)
+          log_error (_("error writing keyring `%s': %s\n"),
+                      keydb_get_resource_name (hd), g10_errstr(rc));
+       else
+         {
+           /* This should not be possible since we delete the
+              ownertrust when a key is deleted, but it can happen if
+              the keyring and trustdb are out of sync.  It can also
+              be made to happen with the trusted-key command. */
+
+           clear_ownertrusts (pk);
+           if(non_self)
+             revalidation_mark ();
+         }
+        keydb_release (hd);
+
        /* we are ready */
        if( !opt.quiet )
-           log_info( _("key %08lX: public key imported\n"), (ulong)keyid[1]);
-       if( is_status_enabled() ) {
+         {
+           char *p=get_user_id_native (keyid);
+           log_info( _("key %s: public key \"%s\" imported\n"),
+                     keystr(keyid),p);
+           xfree(p);
+         }
+       if( is_status_enabled() )
+         {
            char *us = get_long_user_id_string( keyid );
            write_status_text( STATUS_IMPORTED, us );
-           m_free(us);
-       }
-       stats.imported++;
+           xfree(us);
+            print_import_ok (pk,NULL, 1);
+         }
+       stats->imported++;
        if( is_RSA( pk->pubkey_algo ) )
-           stats.imported_rsa++;
+           stats->imported_rsa++;
        new_key = 1;
     }
     else { /* merge */
-       int n_uids, n_sigs, n_subk;
+        KEYDB_HANDLE hd;
+       int n_uids, n_sigs, n_subk, n_sigs_cleaned, n_uids_cleaned;
 
        /* Compare the original against the new key; just to be sure nothing
         * weird is going on */
-       if( cmp_public_keys( pk_orig, pk ) ) {
-           log_error( _("key %08lX: doesn't match our copy\n"),
-                                                         (ulong)keyid[1]);
-           rc = G10ERR_GENERAL;
+       if( cmp_public_keys( pk_orig, pk ) )
+         {
+           log_error( _("key %s: doesn't match our copy\n"),keystr(keyid));
            goto leave;
-       }
+         }
 
        /* now read the original keyblock */
-       rc = find_keyblock_bypk( &kbpos, pk_orig );
-       if( rc ) {
-           log_error( _("key %08lX: can't locate original keyblock: %s\n"),
-                                    (ulong)keyid[1], g10_errstr(rc));
+        hd = keydb_new (0);
+        {
+            byte afp[MAX_FINGERPRINT_LEN];
+            size_t an;
+
+            fingerprint_from_pk (pk_orig, afp, &an);
+            while (an < MAX_FINGERPRINT_LEN) 
+                afp[an++] = 0;
+            rc = keydb_search_fpr (hd, afp);
+        }
+       if( rc )
+         {
+           log_error (_("key %s: can't locate original keyblock: %s\n"),
+                      keystr(keyid), g10_errstr(rc));
+            keydb_release (hd);
            goto leave;
-       }
-       rc = read_keyblock( &kbpos, &keyblock_orig );
-       if( rc ) {
-           log_error( _("key %08lX: can't read original keyblock: %s\n"),
-                                           (ulong)keyid[1], g10_errstr(rc));
+         }
+       rc = keydb_get_keyblock (hd, &keyblock_orig );
+       if (rc)
+         {
+           log_error (_("key %s: can't read original keyblock: %s\n"),
+                      keystr(keyid), g10_errstr(rc));
+            keydb_release (hd);
            goto leave;
-       }
+         }
 
-       collapse_uids( &keyblock );
        /* and try to merge the block */
        clear_kbnode_flags( keyblock_orig );
        clear_kbnode_flags( keyblock );
-       n_uids = n_sigs = n_subk = 0;
+       n_uids = n_sigs = n_subk = n_sigs_cleaned = n_uids_cleaned = 0;
        rc = merge_blocks( fname, keyblock_orig, keyblock,
-                               keyid, &n_uids, &n_sigs, &n_subk );
+                          keyid, &n_uids, &n_sigs, &n_subk );
        if( rc )
+         {
+            keydb_release (hd);
            goto leave;
-       if( n_uids || n_sigs || n_subk ) {
+         }
+
+       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 */
-           if( (rc=lock_keyblock( &kbpos )) )
-              log_error( _("can't lock keyring `%s': %s\n"),
-                         keyblock_resource_name(&kbpos), g10_errstr(rc) );
-           else if( (rc=update_keyblock( &kbpos, keyblock_orig )) )
-               log_error( _("error writing keyring `%s': %s\n"),
-                            keyblock_resource_name(&kbpos), g10_errstr(rc) );
-           unlock_keyblock( &kbpos );
+           rc = keydb_update_keyblock (hd, keyblock_orig);
+            if (rc)
+               log_error (_("error writing keyring `%s': %s\n"),
+                            keydb_get_resource_name (hd), g10_errstr(rc) );
+           else if(non_self)
+             revalidation_mark ();
+
            /* we are ready */
-           if( !opt.quiet ) {
+           if( !opt.quiet )
+             {
+               char *p=get_user_id_native(keyid);
                if( n_uids == 1 )
-                   log_info( _("key %08lX: 1 new user ID\n"),
-                                            (ulong)keyid[1]);
+                 log_info( _("key %s: \"%s\" 1 new user ID\n"),
+                          keystr(keyid),p);
                else if( n_uids )
-                   log_info( _("key %08lX: %d new user IDs\n"),
-                                            (ulong)keyid[1], n_uids );
+                 log_info( _("key %s: \"%s\" %d new user IDs\n"),
+                           keystr(keyid),p,n_uids);
                if( n_sigs == 1 )
-                   log_info( _("key %08lX: 1 new signature\n"),
-                                            (ulong)keyid[1]);
+                 log_info( _("key %s: \"%s\" 1 new signature\n"),
+                           keystr(keyid), p);
                else if( n_sigs )
-                   log_info( _("key %08lX: %d new signatures\n"),
-                                            (ulong)keyid[1], n_sigs );
+                 log_info( _("key %s: \"%s\" %d new signatures\n"),
+                           keystr(keyid), p, n_sigs );
                if( n_subk == 1 )
-                   log_info( _("key %08lX: 1 new subkey\n"),
-                                            (ulong)keyid[1]);
+                 log_info( _("key %s: \"%s\" 1 new subkey\n"),
+                           keystr(keyid), p);
                else if( n_subk )
-                   log_info( _("key %08lX: %d new subkeys\n"),
-                                            (ulong)keyid[1], n_subk );
-           }
-
-           stats.n_uids +=n_uids;
-           stats.n_sigs +=n_sigs;
-           stats.n_subk +=n_subk;
-       }
-       else {
-           if( !opt.quiet )
-               log_info( _("key %08lX: not changed\n"), (ulong)keyid[1] );
-           stats.unchanged++;
+                 log_info( _("key %s: \"%s\" %d new subkeys\n"),
+                           keystr(keyid), p, n_subk );
+               if(n_sigs_cleaned==1)
+                 log_info(_("key %s: \"%s\" %d signature cleaned\n"),
+                          keystr(keyid),p,n_sigs_cleaned);
+               else if(n_sigs_cleaned)
+                 log_info(_("key %s: \"%s\" %d signatures cleaned\n"),
+                          keystr(keyid),p,n_sigs_cleaned);
+               if(n_uids_cleaned==1)
+                 log_info(_("key %s: \"%s\" %d user ID cleaned\n"),
+                          keystr(keyid),p,n_uids_cleaned);
+               else if(n_uids_cleaned)
+                 log_info(_("key %s: \"%s\" %d user IDs cleaned\n"),
+                          keystr(keyid),p,n_uids_cleaned);
+               xfree(p);
+             }
+
+           stats->n_uids +=n_uids;
+           stats->n_sigs +=n_sigs;
+           stats->n_subk +=n_subk;
+           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( !rc && !fast ) {
-       rc = query_trust_record( new_key? pk : pk_orig );
-       if( rc && rc != -1 )
-           log_error("trustdb error: %s\n", g10_errstr(rc) );
-       else if( rc == -1 ) { /* not found trustdb */
-           rc = insert_trust_record( new_key? keyblock : keyblock_orig );
-           if( rc )
-               log_error("key %08lX: trustdb insert failed: %s\n",
-                                       (ulong)keyid[1], g10_errstr(rc) );
-       }
-       else if( mod_key )
-           rc = update_trust_record( keyblock_orig, 1, NULL );
        else
-           rc = clear_trust_checked_flag( new_key? pk : pk_orig );
+         {
+           if (is_status_enabled ()) 
+             print_import_ok (pk, NULL, 0);
+
+           if( !opt.quiet )
+             {
+               char *p=get_user_id_native(keyid);
+               log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p);
+               xfree(p);
+             }
+
+           stats->unchanged++;
+         }
+
+        keydb_release (hd); hd = NULL;
     }
 
   leave:
+
+    /* Now that the key is definitely incorporated into the keydb, we
+       need to check if a designated revocation is present or if the
+       prefs are not rational so we can warn the user. */
+
+    if(mod_key)
+      {
+       revocation_present(keyblock_orig);
+       if(seckey_available(keyid)==0)
+         check_prefs(keyblock_orig);
+      }
+    else if(new_key)
+      {
+       revocation_present(keyblock);
+       if(seckey_available(keyid)==0)
+         check_prefs(keyblock);
+      }
+
     release_kbnode( keyblock_orig );
     free_public_key( pk_orig );
+
     return rc;
 }
 
+/* Walk a secret keyblock and produce a public keyblock out of it. */
+static KBNODE
+sec_to_pub_keyblock(KBNODE sec_keyblock)
+{
+  KBNODE secnode,pub_keyblock=NULL,ctx=NULL;
+
+  while((secnode=walk_kbnode(sec_keyblock,&ctx,0)))
+    {
+      KBNODE pubnode;
+
+      if(secnode->pkt->pkttype==PKT_SECRET_KEY ||
+        secnode->pkt->pkttype==PKT_SECRET_SUBKEY)
+       {
+         /* Make a public key.  We only need to convert enough to
+            write the keyblock out. */
+
+         PKT_secret_key *sk=secnode->pkt->pkt.secret_key;
+         PACKET *pkt=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;
+
+         pkt->pkt.public_key=pk;
+
+         pk->version=sk->version;
+         pk->timestamp=sk->timestamp;
+         pk->expiredate=sk->expiredate;
+         pk->pubkey_algo=sk->pubkey_algo;
+
+         n=pubkey_get_npkey(pk->pubkey_algo);
+         if(n==0)
+           {
+             /* we can't properly extract the pubkey without knowing
+                the number of MPIs */
+             release_kbnode(pub_keyblock);
+             return NULL;
+           }
+         else
+           {
+             int i;
+
+             for(i=0;i<n;i++)
+               pk->pkey[i]=mpi_copy(sk->skey[i]);
+           }
+
+         pubnode=new_kbnode(pkt);
+       }
+      else
+       {
+         pubnode=clone_kbnode(secnode);
+       }
+
+      if(pub_keyblock==NULL)
+       pub_keyblock=pubnode;
+      else
+       add_kbnode(pub_keyblock,pubnode);
+    }
+
+  return pub_keyblock;
+}
 
 /****************
  * Ditto for secret keys.  Handling is simpler than for public keys.
+ * We allow secret key importing only when allow is true, this is so
+ * that a secret key can not be imported accidently and thereby tampering
+ * with the trust calculation.
  */
 static int
-import_secret_one( const char *fname, KBNODE keyblock )
+import_secret_one( const char *fname, KBNODE keyblock, 
+                   struct stats_s *stats, unsigned int options)
 {
     PKT_secret_key *sk;
     KBNODE node, uidnode;
-    KBPOS kbpos;
     u32 keyid[2];
     int rc = 0;
 
@@ -539,52 +1081,108 @@ import_secret_one( const char *fname, KBNODE keyblock )
     keyid_from_sk( sk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if( opt.verbose ) {
-       log_info( "sec  %4u%c/%08lX %s   ",
+    if( opt.verbose )
+      {
+       log_info( "sec  %4u%c/%s %s   ",
                  nbits_from_sk( sk ),
                  pubkey_letter( sk->pubkey_algo ),
-                 (ulong)keyid[1], datestr_from_sk(sk) );
+                 keystr_from_sk(sk), datestr_from_sk(sk) );
        if( uidnode )
-           print_string( stderr, uidnode->pkt->pkt.user_id->name,
-                                 uidnode->pkt->pkt.user_id->len, 0 );
+         print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+                            uidnode->pkt->pkt.user_id->len );
        putc('\n', stderr);
-    }
-    stats.secret_read++;
-    if( !uidnode ) {
-       log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]);
+      }
+    stats->secret_read++;
+
+    if( !uidnode )
+      {
+       log_error( _("key %s: no user ID\n"), keystr_from_sk(sk));
        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;
+      }
+
+#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 
+    
     clear_kbnode_flags( keyblock );
 
     /* do we have this key already in one of our secrings ? */
     rc = seckey_available( keyid );
-    if( rc == G10ERR_NO_SECKEY ) { /* simply insert this key */
+    if( rc == G10ERR_NO_SECKEY && !(opt.import_options&IMPORT_MERGE_ONLY) )
+      {
+       /* simply insert this key */
+        KEYDB_HANDLE hd = keydb_new (1);
+
        /* get default resource */
-       if( get_keyblock_handle( NULL, 1, &kbpos ) ) {
-           log_error("no default secret keyring\n");
-           return G10ERR_GENERAL;
+        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;
        }
-       if( (rc=lock_keyblock( &kbpos )) )
-           log_error( _("can't lock keyring `%s': %s\n"),
-                        keyblock_resource_name(&kbpos), g10_errstr(rc) );
-       else if( (rc=insert_keyblock( &kbpos, keyblock )) )
-           log_error( _("error writing keyring `%s': %s\n"),
-                     keyblock_resource_name(&kbpos), g10_errstr(rc) );
-       unlock_keyblock( &kbpos );
+       rc = keydb_insert_keyblock (hd, keyblock );
+        if (rc)
+         log_error (_("error writing keyring `%s': %s\n"),
+                    keydb_get_resource_name (hd), g10_errstr(rc) );
+        keydb_release (hd);
        /* we are ready */
        if( !opt.quiet )
-           log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]);
-       stats.secret_imported++;
-    }
-    else if( !rc ) { /* we can't merge secret keys */
-       log_error( _("key %08lX: already in secret keyring\n"),
-                                                       (ulong)keyid[1]);
-       stats.secret_dups++;
-    }
+         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);
+
+       if(options&IMPORT_SK2PK)
+         {
+           /* Try and make a public key out of this. */
+
+           KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
+           if(pub_keyblock)
+             {
+               import_one(fname,pub_keyblock,stats,
+                          NULL,NULL,opt.import_options);
+               release_kbnode(pub_keyblock);
+             }
+         }
+
+       /* 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 %08lX: secret key not found: %s\n"),
-                               (ulong)keyid[1], g10_errstr(rc));
+      log_error( _("key %s: secret key not found: %s\n"),
+                keystr_from_sk(sk), g10_errstr(rc));
 
     return rc;
 }
@@ -594,11 +1192,11 @@ import_secret_one( const char *fname, KBNODE keyblock )
  * Import a revocation certificate; this is a single signature packet.
  */
 static int
-import_revoke_cert( const char *fname, KBNODE node )
+import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
 {
     PKT_public_key *pk=NULL;
     KBNODE onode, keyblock = NULL;
-    KBPOS kbpos;
+    KEYDB_HANDLE hd = NULL;
     u32 keyid[2];
     int rc = 0;
 
@@ -609,56 +1207,69 @@ import_revoke_cert( const char *fname, KBNODE node )
     keyid[0] = node->pkt->pkt.signature->keyid[0];
     keyid[1] = node->pkt->pkt.signature->keyid[1];
 
-    pk = m_alloc_clear( sizeof *pk );
+    pk = xmalloc_clear( sizeof *pk );
     rc = get_pubkey( pk, keyid );
-    if( rc == G10ERR_NO_PUBKEY ) {
-       log_info( _("key %08lX: no public key - "
-                "can't apply revocation certificate\n"), (ulong)keyid[1]);
+    if( rc == G10ERR_NO_PUBKEY )
+      {
+       log_error(_("key %s: no public key -"
+                   " can't apply revocation certificate\n"), keystr(keyid));
        rc = 0;
        goto leave;
-    }
-    else if( rc ) {
-       log_error( _("key %08lX: public key not found: %s\n"),
-                                      (ulong)keyid[1], g10_errstr(rc));
+      }
+    else if( rc )
+      {
+       log_error(_("key %s: public key not found: %s\n"),
+                 keystr(keyid), g10_errstr(rc));
        goto leave;
-    }
+      }
 
     /* read the original keyblock */
-    rc = find_keyblock_bypk( &kbpos, pk );
-    if( rc ) {
-       log_error( _("key %08lX: can't locate original keyblock: %s\n"),
-                                       (ulong)keyid[1], g10_errstr(rc));
-       goto leave;
+    hd = keydb_new (0);
+    {
+        byte afp[MAX_FINGERPRINT_LEN];
+        size_t an;
+        
+        fingerprint_from_pk (pk, afp, &an);
+        while (an < MAX_FINGERPRINT_LEN) 
+            afp[an++] = 0;
+        rc = keydb_search_fpr (hd, afp);
     }
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error( _("key %08lX: can't read original keyblock: %s\n"),
-                                       (ulong)keyid[1], g10_errstr(rc));
+    if (rc)
+      {
+       log_error (_("key %s: can't locate original keyblock: %s\n"),
+                   keystr(keyid), g10_errstr(rc));
        goto leave;
-    }
-
+      }
+    rc = keydb_get_keyblock (hd, &keyblock );
+    if (rc)
+      {
+       log_error (_("key %s: can't read original keyblock: %s\n"),
+                   keystr(keyid), g10_errstr(rc));
+       goto leave;
+      }
 
     /* it is okay, that node is not in keyblock because
      * check_key_signature works fine for sig_class 0x20 in this
      * special case. */
     rc = check_key_signature( keyblock, node, NULL);
-    if( rc ) {
-       log_error( _("key %08lX: invalid revocation certificate"
-                 ": %s - rejected\n"), (ulong)keyid[1], g10_errstr(rc));
-    }
-
+    if( rc )
+      {
+       log_error( _("key %s: invalid revocation certificate"
+                    ": %s - rejected\n"), keystr(keyid), g10_errstr(rc));
+       goto leave;
+      }
 
     /* check whether we already have this */
     for(onode=keyblock->next; onode; onode=onode->next ) {
        if( onode->pkt->pkttype == PKT_USER_ID )
            break;
        else if( onode->pkt->pkttype == PKT_SIGNATURE
-                && onode->pkt->pkt.signature->sig_class == 0x20
-                && keyid[0] == onode->pkt->pkt.signature->keyid[0]
-                && keyid[1] == onode->pkt->pkt.signature->keyid[1] ) {
+                && !cmp_signatures(node->pkt->pkt.signature,
+                                   onode->pkt->pkt.signature))
+         {
            rc = 0;
            goto leave; /* yes, we already know about it */
-       }
+         }
     }
 
 
@@ -666,20 +1277,31 @@ import_revoke_cert( const char *fname, KBNODE node )
     insert_kbnode( keyblock, clone_kbnode(node), 0 );
 
     /* and write the keyblock back */
-    if( (rc=lock_keyblock( &kbpos )) )
-       log_error( _("can't lock keyring `%s': %s\n"),
-                  keyblock_resource_name(&kbpos), g10_errstr(rc) );
-    else if( (rc=update_keyblock( &kbpos, keyblock )) )
-       log_error( _("error writing keyring `%s': %s\n"),
-                   keyblock_resource_name(&kbpos), g10_errstr(rc) );
-    unlock_keyblock( &kbpos );
+    rc = keydb_update_keyblock (hd, keyblock );
+    if (rc)
+       log_error (_("error writing keyring `%s': %s\n"),
+                   keydb_get_resource_name (hd), g10_errstr(rc) );
+    keydb_release (hd); hd = NULL;
     /* we are ready */
     if( !opt.quiet )
-       log_info( _("key %08lX: revocation certificate imported\n"),
-                                       (ulong)keyid[1]);
-    stats.n_revoc++;
+      {
+        char *p=get_user_id_native (keyid);
+       log_info( _("key %s: \"%s\" revocation certificate imported\n"),
+                 keystr(keyid),p);
+       xfree(p);
+      }
+    stats->n_revoc++;
+
+    /* If the key we just revoked was ultimately trusted, remove its
+       ultimate trust.  This doesn't stop the user from putting the
+       ultimate trust back, but is a reasonable solution for now. */
+    if(get_ownertrust(pk)==TRUST_ULTIMATE)
+      clear_ownertrusts(pk);
+
+    revalidation_mark ();
 
   leave:
+    keydb_release (hd);
     release_kbnode( keyblock );
     free_public_key( pk );
     return rc;
@@ -690,90 +1312,177 @@ import_revoke_cert( const char *fname, KBNODE node )
  * loop over the keyblock and check all self signatures.
  * Mark all user-ids with a self-signature by setting flag bit 0.
  * Mark all user-ids with an invalid self-signature by setting bit 1.
- * This works also for subkeys, here the subkey is marked.
+ * This works also for subkeys, here the subkey is marked.  Invalid or
+ * extra subkey sigs (binding or revocation) are marked for deletion.
+ * non_self is set to true if there are any sigs other than self-sigs
+ * in this keyblock.
  */
 static int
 chk_self_sigs( const char *fname, KBNODE keyblock,
-              PKT_public_key *pk, u32 *keyid )
+              PKT_public_key *pk, u32 *keyid, int *non_self )
 {
-    KBNODE n;
+    KBNODE n,knode=NULL;
     PKT_signature *sig;
     int rc;
+    u32 bsdate=0,rsdate=0;
+    KBNODE bsnode=NULL,rsnode=NULL;
 
     for( n=keyblock; (n = find_next_kbnode(n, 0)); ) {
-       if( n->pkt->pkttype != PKT_SIGNATURE )
+      if(n->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+       {
+         knode=n;
+         bsdate=0;
+         rsdate=0;
+         bsnode=NULL;
+         rsnode=NULL;
+         continue;
+       }
+      else if( n->pkt->pkttype != PKT_SIGNATURE )
            continue;
        sig = n->pkt->pkt.signature;
        if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
-           if( (sig->sig_class&~3) == 0x10 ) {
+
+           /* 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( IS_UID_SIG(sig) || IS_UID_REV(sig) )
+             {
                KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
-               if( !unode )  {
-                   log_error( _("key %08lX: no user ID for signature\n"),
-                                           (ulong)keyid[1]);
+               if( !unode )
+                 {
+                   log_error( _("key %s: no user ID for signature\n"),
+                              keystr(keyid));
                    return -1;  /* the complete keyblock is invalid */
+                 }
+
+               /* If it hasn't been marked valid yet, keep trying */
+               if(!(unode->flag&1)) {
+                 rc = check_key_signature( keyblock, n, NULL);
+                 if( rc )
+                   {
+                     if( opt.verbose )
+                       {
+                         char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+                                     strlen(unode->pkt->pkt.user_id->name),0);
+                         log_info( rc == G10ERR_PUBKEY_ALGO ?
+                                   _("key %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 */
                }
-               rc = check_key_signature( keyblock, n, NULL);
-               if( rc ) {
-                   log_info( rc == G10ERR_PUBKEY_ALGO ?
-                        _("key %08lX: unsupported public key algorithm\n"):
-                        _("key %08lX: invalid self-signature\n"),
-                                    (ulong)keyid[1]);
-
-                   unode->flag |= 2; /* mark as invalid */
-               }
-               unode->flag |= 1; /* mark that signature checked */
-           }
+             }
            else if( sig->sig_class == 0x18 ) {
-               KBNODE knode = find_prev_kbnode( keyblock,
-                                                n, PKT_PUBLIC_SUBKEY );
-               if( !knode )
-                   knode = find_prev_kbnode( keyblock,
-                                                n, PKT_SECRET_SUBKEY );
+             /* Note that this works based solely on the timestamps
+                like the rest of gpg.  If the standard gets
+                revocation targets, this may need to be revised. */
 
-               if( !knode ) {
-                   log_info( _("key %08lX: no subkey for key binding\n"),
-                                           (ulong)keyid[1]);
+               if( !knode )
+                 {
+                   if(opt.verbose)
+                     log_info( _("key %s: no subkey for key binding\n"),
+                               keystr(keyid));
                    n->flag |= 4; /* delete this */
-               }
-               else {
+                 }
+               else
+                 {
                    rc = check_key_signature( keyblock, n, NULL);
-                   if( rc ) {
-                       log_info(  rc == G10ERR_PUBKEY_ALGO ?
-                          _("key %08lX: unsupported public key algorithm\n"):
-                          _("key %08lX: invalid subkey binding\n"),
-                                        (ulong)keyid[1]);
-
-                       knode->flag |= 2; /* mark as invalid */
-                   }
-                   knode->flag |= 1; /* mark that signature checked */
-               }
+                   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));
+                           }
+
+                         bsnode=n;
+                         bsdate=sig->timestamp;
+                       }
+                       else
+                         n->flag|=4; /* older */
+                     }
+                 }
            }
-       }
-    }
-    return 0;
-}
-
-
-
-/****************
- * If a user ID has at least one signature, mark it as valid
- */
-static void
-mark_non_selfsigned_uids_valid( KBNODE keyblock, u32 *kid )
-{
-    KBNODE node;
-    for(node=keyblock->next; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) ) {
-           if( node->next && node->next->pkt->pkttype == PKT_SIGNATURE ) {
-               node->flag |= 1;
-               log_info( _("key %08lX: accepted non self-signed user ID '"),
-                                                        (ulong)kid[1]);
-               print_string( log_stream(), node->pkt->pkt.user_id->name,
-                                           node->pkt->pkt.user_id->len, 0 );
-               fputs("'\n", log_stream() );
+           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;
     }
+
+    return 0;
 }
 
 /****************
@@ -784,23 +1493,24 @@ mark_non_selfsigned_uids_valid( KBNODE keyblock, u32 *kid )
  * returns: true if at least one valid user-id is left over.
  */
 static int
-delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
+delete_inv_parts( const char *fname, KBNODE keyblock,
+                 u32 *keyid, unsigned int options)
 {
     KBNODE node;
-    int nvalid=0, uid_seen=0;
-    const char *p;
+    int nvalid=0, uid_seen=0, subkey_seen=0;
 
     for(node=keyblock->next; node; node = node->next ) {
        if( node->pkt->pkttype == PKT_USER_ID ) {
            uid_seen = 1;
            if( (node->flag & 2) || !(node->flag & 1) ) {
-               if( opt.verbose ) {
-                   log_info( _("key %08lX: skipped user ID '"),
-                                                        (ulong)keyid[1]);
-                   print_string( stderr, node->pkt->pkt.user_id->name,
-                                     node->pkt->pkt.user_id->len, 0 );
-                   fputs("'\n", stderr );
-               }
+               if( opt.verbose )
+                 {
+                   char *p=utf8_to_native(node->pkt->pkt.user_id->name,
+                                          node->pkt->pkt.user_id->len,0);
+                   log_info( _("key %s: skipped user ID \"%s\"\n"),
+                             keystr(keyid),p);
+                   xfree(p);
+                 }
                delete_kbnode( node ); /* the user-id */
                /* and all following packets up to the next user-id */
                while( node->next
@@ -817,10 +1527,9 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
        else if(    node->pkt->pkttype == PKT_PUBLIC_SUBKEY
                 || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
            if( (node->flag & 2) || !(node->flag & 1) ) {
-               if( opt.verbose ) {
-                   log_info( _("key %08lX: skipped subkey\n"),
-                                                        (ulong)keyid[1]);
-               }
+               if( opt.verbose )
+                 log_info( _("key %s: skipped subkey\n"),keystr(keyid));
+
                delete_kbnode( node ); /* the subkey */
                /* and all following signature packets */
                while( node->next
@@ -829,46 +1538,79 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid )
                    node = node->next;
                }
            }
+           else
+             subkey_seen = 1;
        }
        else if( node->pkt->pkttype == PKT_SIGNATURE
-                && openpgp_pk_test_algo( node->pkt->pkt.signature->pubkey_algo, 0)
-                && node->pkt->pkt.signature->pubkey_algo != GCRY_PK_RSA )
+                && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo)
+                && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
            delete_kbnode( node ); /* build_packet() can't handle this */
-       else if( node->pkt->pkttype == PKT_SIGNATURE
-                && (p = parse_sig_subpkt2( node->pkt->pkt.signature,
-                                           SIGSUBPKT_EXPORTABLE, NULL ))
-                && !*p
-                && seckey_available( node->pkt->pkt.signature->keyid ) ) {
+       else if( node->pkt->pkttype == PKT_SIGNATURE &&
+                !node->pkt->pkt.signature->flags.exportable &&
+                !(options&IMPORT_LOCAL_SIGS) &&
+                seckey_available( node->pkt->pkt.signature->keyid ) )
+         {
            /* here we violate the rfc a bit by still allowing
             * to import non-exportable signature when we have the
             * the secret key used to create this signature - it
             * seems that this makes sense */
-           log_info( _("key %08lX: non exportable signature "
-                                   "(class %02x) - skipped\n"),
-                                   (ulong)keyid[1],
-                                    node->pkt->pkt.signature->sig_class );
+           if(opt.verbose)
+             log_info( _("key %s: non exportable signature"
+                         " (class 0x%02X) - skipped\n"),
+                       keystr(keyid), node->pkt->pkt.signature->sig_class );
            delete_kbnode( node );
-       }
+         }
        else if( node->pkt->pkttype == PKT_SIGNATURE
                 && node->pkt->pkt.signature->sig_class == 0x20 )  {
-           if( uid_seen ) {
-               log_error( _("key %08lX: revocation certificate "
-                                    "at wrong place - skipped\n"),
-                                   (ulong)keyid[1]);
+           if( uid_seen )
+             {
+               if(opt.verbose)
+                 log_info( _("key %s: revocation certificate"
+                             " at wrong place - skipped\n"),keystr(keyid));
                delete_kbnode( node );
-           }
+             }
            else {
-               int rc = check_key_signature( keyblock, node, NULL);
-               if( rc ) {
-                   log_error( _("key %08lX: invalid revocation "
-                             "certificate: %s - skipped\n"),
-                             (ulong)keyid[1], g10_errstr(rc));
-                   delete_kbnode( node );
+             /* If the revocation cert is from a different key than
+                 the one we're working on don't check it - it's
+                 probably from a revocation key and won't be
+                 verifiable with this key anyway. */
+
+             if(node->pkt->pkt.signature->keyid[0]==keyid[0] &&
+                node->pkt->pkt.signature->keyid[1]==keyid[1])
+               {
+                 int rc = check_key_signature( keyblock, node, NULL);
+                 if( rc )
+                   {
+                     if(opt.verbose)
+                       log_info( _("key %s: invalid revocation"
+                                   " certificate: %s - skipped\n"),
+                                 keystr(keyid), g10_errstr(rc));
+                     delete_kbnode( node );
+                   }
                }
            }
        }
-       else if( (node->flag & 4) ) /* marked for deletion */
+       else if( node->pkt->pkttype == PKT_SIGNATURE &&
+                (node->pkt->pkt.signature->sig_class == 0x18 ||
+                 node->pkt->pkt.signature->sig_class == 0x28) &&
+                !subkey_seen )
+         {
+           if(opt.verbose)
+             log_info( _("key %s: subkey signature"
+                         " in wrong place - skipped\n"), keystr(keyid));
            delete_kbnode( node );
+         }
+       else if( node->pkt->pkttype == PKT_SIGNATURE
+                && !IS_CERT(node->pkt->pkt.signature))
+         {
+           if(opt.verbose)
+             log_info(_("key %s: unexpected signature class (0x%02X) -"
+                        " skipped\n"),keystr(keyid),
+                      node->pkt->pkt.signature->sig_class);
+           delete_kbnode(node);
+         }
+       else if( (node->flag & 4) ) /* marked for deletion */
+         delete_kbnode( node );
     }
 
     /* note: because keyblock is the public key, it is never marked
@@ -890,7 +1632,6 @@ collapse_uids( KBNODE *keyblock )
     KBNODE n, n2;
     int in_uid;
     int any=0;
-    u32 kid1;
 
   restart:
     for( n = *keyblock; n; n = n->next ) {
@@ -954,19 +1695,103 @@ collapse_uids( KBNODE *keyblock )
        }
     }
 
-    if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
-       kid1 = keyid_from_pk( n->pkt->pkt.public_key, NULL );
-    else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
-       kid1 = keyid_from_sk( n->pkt->pkt.secret_key, NULL );
-    else
-       kid1 = 0;
-    log_info(_("key %08lX: duplicated user ID detected - merged\n"),
-                                                                (ulong)kid1);
+    if(!opt.quiet)
+      {
+       const char *key="???";
+
+       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);
+
+       log_info(_("key %s: duplicated user ID detected - merged\n"),key);
+      }
 
     return 1;
 }
 
+/* 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)
+{
+  KBNODE onode,inode;
+  PKT_public_key *pk=keyblock->pkt->pkt.public_key;
+
+  for(onode=keyblock->next;onode;onode=onode->next)
+    {
+      /* If we reach user IDs, we're done. */
+      if(onode->pkt->pkttype==PKT_USER_ID)
+       break;
+
+      if(onode->pkt->pkttype==PKT_SIGNATURE &&
+        onode->pkt->pkt.signature->sig_class==0x1F &&
+        onode->pkt->pkt.signature->revkey)
+       {
+         int idx;
+         PKT_signature *sig=onode->pkt->pkt.signature;
+
+         for(idx=0;idx<sig->numrevkeys;idx++)
+           {
+             u32 keyid[2];
+
+             keyid_from_fingerprint(sig->revkey[idx]->fpr,
+                                    MAX_FINGERPRINT_LEN,keyid);
+
+             for(inode=keyblock->next;inode;inode=inode->next)
+               {
+                 /* If we reach user IDs, we're done. */
+                 if(inode->pkt->pkttype==PKT_USER_ID)
+                   break;
 
+                 if(inode->pkt->pkttype==PKT_SIGNATURE &&
+                    inode->pkt->pkt.signature->sig_class==0x20 &&
+                    inode->pkt->pkt.signature->keyid[0]==keyid[0] &&
+                    inode->pkt->pkt.signature->keyid[1]==keyid[1])
+                   {
+                     /* Okay, we have a revocation key, and a
+                         revocation issued by it.  Do we have the key
+                         itself? */
+                     int rc;
+
+                     rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr,
+                                                   MAX_FINGERPRINT_LEN);
+                     if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
+                       {
+                         char *tempkeystr=xstrdup(keystr_from_pk(pk));
+
+                         /* No, so try and get it */
+                         if(opt.keyserver
+                            && (opt.keyserver_options.options
+                                & KEYSERVER_AUTO_KEY_RETRIEVE))
+                           {
+                             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);
+
+                             /* Do we have it now? */
+                             rc=get_pubkey_byfprint_fast (NULL,
+                                                    sig->revkey[idx]->fpr,
+                                                    MAX_FINGERPRINT_LEN);
+                           }
+
+                         if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
+                           log_info(_("WARNING: key %s may be revoked:"
+                                      " revocation key %s not present.\n"),
+                                    tempkeystr,keystr(keyid));
+
+                         xfree(tempkeystr);
+                       }
+                   }
+               }
+           }
+       }
+    }
+}
 
 /****************
  * compare and merge the blocks
@@ -999,25 +1824,62 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                    break;
                else if( onode->pkt->pkttype == PKT_SIGNATURE
                         && onode->pkt->pkt.signature->sig_class == 0x20
-                        && node->pkt->pkt.signature->keyid[0]
-                           == onode->pkt->pkt.signature->keyid[0]
-                        && node->pkt->pkt.signature->keyid[1]
-                           == onode->pkt->pkt.signature->keyid[1] ) {
+                        && !cmp_signatures(onode->pkt->pkt.signature,
+                                           node->pkt->pkt.signature))
+                 {
                    found = 1;
                    break;
-               }
+                 }
            }
            if( !found ) {
                KBNODE n2 = clone_kbnode(node);
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
-               log_info( _("key %08lX: revocation certificate added\n"),
-                                        (ulong)keyid[1]);
+                ++*n_sigs;
+               if(!opt.quiet)
+                 {
+                   char *p=get_user_id_native (keyid);
+                   log_info(_("key %s: \"%s\" revocation"
+                              " certificate added\n"), keystr(keyid),p);
+                   xfree(p);
+                 }
+           }
+       }
+    }
+
+    /* 2nd: merge in any direct key (0x1F) sigs */
+    for(node=keyblock->next; node; node=node->next ) {
+       if( node->pkt->pkttype == PKT_USER_ID )
+           break;
+       else if( node->pkt->pkttype == PKT_SIGNATURE
+                && node->pkt->pkt.signature->sig_class == 0x1F )  {
+           /* check whether we already have this */
+           found = 0;
+           for(onode=keyblock_orig->next; onode; onode=onode->next ) {
+               if( onode->pkt->pkttype == PKT_USER_ID )
+                   break;
+               else if( onode->pkt->pkttype == PKT_SIGNATURE
+                        && onode->pkt->pkt.signature->sig_class == 0x1F
+                        && !cmp_signatures(onode->pkt->pkt.signature,
+                                           node->pkt->pkt.signature)) {
+                   found = 1;
+                   break;
+               }
            }
+           if( !found )
+             {
+               KBNODE n2 = clone_kbnode(node);
+               insert_kbnode( keyblock_orig, n2, 0 );
+               n2->flag |= 1;
+                ++*n_sigs;
+               if(!opt.quiet)
+                 log_info( _("key %s: direct key signature added\n"),
+                           keystr(keyid));
+             }
        }
     }
 
-    /* 2nd: try to merge new certificates in */
+    /* 3rd: try to merge new certificates in */
     for(onode=keyblock_orig->next; onode; onode=onode->next ) {
        if( !(onode->flag & 1) && onode->pkt->pkttype == PKT_USER_ID) {
            /* find the user id in the imported keyblock */
@@ -1034,7 +1896,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
        }
     }
 
-    /* 3rd: add new user-ids */
+    /* 4th: add new user-ids */
     for(node=keyblock->next; node; node=node->next ) {
        if( node->pkt->pkttype == PKT_USER_ID) {
            /* do we have this in the original keyblock */
@@ -1052,7 +1914,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
        }
     }
 
-    /* add new subkeys */
+    /* 5th: add new subkeys */
     for(node=keyblock->next; node; node=node->next ) {
        onode = NULL;
        if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
@@ -1085,7 +1947,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
        }
     }
 
-    /* merge subkey certificates */
+    /* 6th: merge subkey certificates */
     for(onode=keyblock_orig->next; onode; onode=onode->next ) {
        if( !(onode->flag & 1)
            &&  (   onode->pkt->pkttype == PKT_PUBLIC_SUBKEY
@@ -1124,11 +1986,6 @@ append_uid( KBNODE keyblock, KBNODE node, int *n_sigs,
     KBNODE n, n_where=NULL;
 
     assert(node->pkt->pkttype == PKT_USER_ID );
-    if( !node->next || node->next->pkt->pkttype == PKT_USER_ID ) {
-       log_error( _("key %08lX: our copy has no self-signature\n"),
-                                                 (ulong)keyid[1]);
-       return G10ERR_GENERAL;
-    }
 
     /* find the position */
     for( n = keyblock; n; n_where = n, n = n->next ) {
@@ -1177,12 +2034,6 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
 
     assert(dst->pkt->pkttype == PKT_USER_ID );
     assert(src->pkt->pkttype == PKT_USER_ID );
-    if( !dst->next || dst->next->pkt->pkttype == PKT_USER_ID ) {
-       log_error( _("key %08lX: our copy has no self-signature\n"),
-                                                 (ulong)keyid[1]);
-       return 0;
-    }
-
 
     for(n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next ) {
        if( n->pkt->pkttype != PKT_SIGNATURE )
@@ -1191,20 +2042,12 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
            || n->pkt->pkt.signature->sig_class == 0x28 )
            continue; /* skip signatures which are only valid on subkeys */
        found = 0;
-       for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next){
-           if( n2->pkt->pkttype == PKT_SIGNATURE
-               && n->pkt->pkt.signature->keyid[0]
-                  == n2->pkt->pkt.signature->keyid[0]
-               && n->pkt->pkt.signature->keyid[1]
-                  == n2->pkt->pkt.signature->keyid[1]
-               && n->pkt->pkt.signature->timestamp
-                  <= n2->pkt->pkt.signature->timestamp
-               && n->pkt->pkt.signature->sig_class
-                  == n2->pkt->pkt.signature->sig_class ) {
-               found++;
-               break;
+       for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next)
+         if(!cmp_signatures(n->pkt->pkt.signature,n2->pkt->pkt.signature))
+           {
+             found++;
+             break;
            }
-       }
        if( !found ) {
            /* This signature is new or newer, append N to DST.
             * We add a clone to the original keyblock, because this
@@ -1303,3 +2146,231 @@ 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;
+}
+