* options.h, g10.c (main), import.c (parse_import_options, import_one,
[gnupg.git] / g10 / import.c
index 4be313f..2168fe5 100644 (file)
@@ -1,5 +1,6 @@
 /* import.c
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ *               2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -34,6 +35,7 @@
 #include "trustdb.h"
 #include "main.h"
 #include "i18n.h"
+#include "ttyio.h"
 #include "status.h"
 #include "keyserver-internal.h"
 
@@ -51,22 +53,22 @@ struct stats_s {
     ulong secret_imported;
     ulong secret_dups;
     ulong skipped_new_keys;
+    ulong not_imported;
 };
 
 
-static int import( IOBUF inp, int fast, const char* fname,
+static int import( IOBUF inp, const char* fname,
                    struct stats_s *stats, unsigned int options );
 static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
 static void revocation_present(KBNODE keyblock);
-static void remove_bad_stuff (KBNODE keyblock);
-static int import_one( const char *fname, KBNODE keyblock, int fast,
+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 );
+                              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 );
+                         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,
@@ -81,51 +83,21 @@ 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)
+parse_import_options(char *str,unsigned int *options,int noisy)
 {
-  char *tok;
-  int hit=0;
-  struct
-  {
-    char *name;
-    unsigned int bit;
-  } import_opts[]=
+  struct parse_options import_opts[]=
     {
       {"allow-local-sigs",IMPORT_ALLOW_LOCAL_SIGS},
-      {"repair-hkp-subkey-bug",IMPORT_REPAIR_HKP_SUBKEY_BUG},
+      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG},
+      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG},
+      {"fast-import",IMPORT_FAST_IMPORT},
+      {"convert-sk-to-pk",IMPORT_SK2PK},
+      {"merge-only",IMPORT_MERGE_ONLY},
       {NULL,0}
     };
 
-  while((tok=strsep(&str," ,")))
-    {
-      int i,rev=0;
-
-      if(ascii_memcasecmp("no-",tok,3)==0)
-       {
-         rev=1;
-         tok+=3;
-       }
-
-      for(i=0;import_opts[i].name;i++)
-       {
-         if(ascii_strcasecmp(import_opts[i].name,tok)==0)
-           {
-             if(rev)
-               *options&=~import_opts[i].bit;
-             else
-               *options|=import_opts[i].bit;
-             hit=1;
-             break;
-           }
-       }
-
-      if(!hit && !import_opts[i].name)
-       return 0;
-    }
-
-  return hit;
+  return parse_options(str,options,import_opts,noisy);
 }
 
 void *
@@ -171,64 +143,78 @@ import_release_stats_handle (void *p)
  *  Key revocation certificates have special handling.
  *
  */
-void
-import_keys( char **fnames, int nnames, int fast,
-            void *stats_handle, unsigned int options )
+static int
+import_keys_internal( IOBUF inp, char **fnames, int nnames,
+                     void *stats_handle, unsigned int options )
 {
-    int i;
+    int i, rc = 0;
     struct stats_s *stats = stats_handle;
 
     if (!stats)
         stats = import_new_stats_handle ();
 
-    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 inp = iobuf_open(fname);
-       if( !fname )
-           fname = "[stdin]";
-       if( !inp )
-           log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
-       else {
-           int rc = import( inp, fast, fname, stats, options );
-           iobuf_close(inp);
-           if( rc )
-               log_error("import from `%s' failed: %s\n", fname,
-                                                          g10_errstr(rc) );
+    if (inp) {
+        rc = import( inp, "[stream]", stats, options);
+    }
+    else {
+        if( !fnames && !nnames )
+           nnames = 1;  /* Ohh what a ugly hack to jump into the loop */
+
+       for(i=0; i < nnames; i++ ) {
+           const char *fname = fnames? fnames[i] : NULL;
+           IOBUF inp2 = iobuf_open(fname);
+           if( !fname )
+               fname = "[stdin]";
+           if( !inp2 )
+               log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+           else {
+               rc = import( inp2, fname, stats, options );
+               iobuf_close(inp2);
+                /* Must invalidate that ugly cache to actually close it. */
+                iobuf_ioctl (NULL, 2, 0, (char*)fname);
+               if( rc )
+                   log_error("import from `%s' failed: %s\n", fname,
+                                     g10_errstr(rc) );
+           }
+           if( !fname )
+               break;
        }
-       if( !fname )
-           break;
     }
     if (!stats_handle) {
         import_print_stats (stats);
         import_release_stats_handle (stats);
     }
+    /* If no fast import and the trustdb is dirty (i.e. we added a key
+       or userID that had something other than a selfsig, a signature
+       that was other than a selfsig, or any revocation), then
+       update/check the trustdb if the user specified by setting
+       interactive or by not setting no-auto-check-trustdb */
+    if (!(options&IMPORT_FAST_IMPORT) && trustdb_pending_check())
+      {
+       if (opt.interactive)
+         update_trustdb();
+       else if (!opt.no_auto_check_trustdb)
+         check_trustdb();
+      }
 
+    return rc;
 }
 
-int
-import_keys_stream( IOBUF inp, int fast,
-                   void *stats_handle, unsigned int options )
+void
+import_keys( char **fnames, int nnames,
+            void *stats_handle, unsigned int options )
 {
-    int rc = 0;
-    struct stats_s *stats = stats_handle;
-
-    if (!stats)
-        stats = import_new_stats_handle ();
-
-    rc = import( inp, fast, "[stream]", stats, options);
-    if (!stats_handle) {
-        import_print_stats (stats);
-        import_release_stats_handle (stats);
-    }
+    import_keys_internal( NULL, fnames, nnames, stats_handle, options);
+}
 
-    return rc;
+int
+import_keys_stream( IOBUF inp, void *stats_handle, unsigned int options )
+{
+    return import_keys_internal( inp, NULL, 0, stats_handle, options);
 }
 
 static int
-import( IOBUF inp, int fast, const char* fname,
+import( IOBUF inp, const char* fname,
        struct stats_s *stats, unsigned int options )
 {
     PACKET *pending_pkt = NULL;
@@ -244,11 +230,10 @@ import( IOBUF inp, int fast, const char* fname,
     }
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
-        remove_bad_stuff (keyblock);
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, fast, stats, options );
+           rc = import_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
-                rc = import_secret_one( fname, keyblock, stats );
+                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, stats );
@@ -257,10 +242,12 @@ import( IOBUF inp, int fast, const char* fname,
                                            keyblock->pkt->pkttype );
        }
        release_kbnode(keyblock);
+        /* fixme: we should increment the not imported counter but this
+           does only make sense if we keep on going despite of errors. */
        if( rc )
            break;
        if( !(++stats->count % 100) && !opt.quiet )
-           log_info(_("%lu keys so far processed\n"), stats->count );
+           log_info(_("%lu keys processed so far\n"), stats->count );
     }
     if( rc == -1 )
        rc = 0;
@@ -305,11 +292,13 @@ import_print_stats (void *hd)
            log_info(_("  secret keys imported: %lu\n"), stats->secret_imported );
        if( stats->secret_dups )
            log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups );
+       if( stats->not_imported )
+           log_info(_("          not imported: %lu\n"), stats->not_imported );
     }
 
     if( is_status_enabled() ) {
-       char buf[13*20];
-       sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+       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,
@@ -322,7 +311,8 @@ import_print_stats (void *hd)
                stats->secret_read,
                stats->secret_imported,
                stats->secret_dups,
-               stats->skipped_new_keys );
+               stats->skipped_new_keys,
+                stats->not_imported );
        write_status_text( STATUS_IMPORT_RES, buf );
     }
 }
@@ -375,17 +365,17 @@ 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;
-           }
-           {
+             }
+           else
+             {
                compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx );
-               cfx->algo = pkt->pkt.compressed->algorithm;
                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;
@@ -429,23 +419,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     return rc;
 }
 
-
-static void
-remove_bad_stuff (KBNODE keyblock)
-{
-    KBNODE node;
-
-    for (node=keyblock; node; node = node->next ) {
-        if( node->pkt->pkttype == PKT_SIGNATURE ) {
-            /* delete the subpackets we used to use for the
-               verification cache */
-            delete_sig_subpkt (node->pkt->pkt.signature->unhashed,
-                               SIGSUBPKT_PRIV_VERIFY_CACHE);
-        }
-    }
-}
-
-/* Walk through the subkeys on a pk to find if we have the HKP
+/* 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
@@ -455,7 +429,7 @@ remove_bad_stuff (KBNODE keyblock)
    sub2 sub3".  Returns TRUE if the keyblock was modified. */
 
 static int
-fix_hkp_corruption(KBNODE keyblock)
+fix_pks_corruption(KBNODE keyblock)
 {
   int changed=0,keycount=0;
   KBNODE node,last=NULL,sknode=NULL;
@@ -486,6 +460,10 @@ fix_hkp_corruption(KBNODE keyblock)
          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. */
@@ -509,95 +487,49 @@ fix_hkp_corruption(KBNODE keyblock)
   return changed;
 }
 
-/* Clean the subkeys on a pk so that they each have at most 1 binding
-   sig and at most 1 revocation sig.  This works based solely on the
-   timestamps like the rest of gpg.  If the standard does get
-   revocation targets, this may need to be revised. */
 
-static int
-clean_subkeys(KBNODE keyblock,u32 *keyid)
+static void
+print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
 {
-  int removed=0;
-  KBNODE node,sknode=keyblock;
-
-  while((sknode=find_kbnode(sknode,PKT_PUBLIC_SUBKEY)))
-    {
-      KBNODE bsnode=NULL,rsnode=NULL;
-      u32 bsdate=0,rsdate=0;
-
-      sknode=sknode->next;
-
-      for(node=sknode;node;node=node->next)
-       {
-         if(node->pkt->pkttype==PKT_SIGNATURE)
-           {
-             PKT_signature *sig=node->pkt->pkt.signature;
-
-             /* We're only interested in valid sigs */
-             if(check_key_signature(keyblock,node,NULL))
-               continue;
-
-             if(IS_SUBKEY_SIG(sig) && bsdate<=sig->timestamp)
-               {
-                 bsnode=node;
-                 bsdate=sig->timestamp;
-               }
-             else if(IS_SUBKEY_REV(sig) && rsdate<=sig->timestamp)
-               {
-                 rsnode=node;
-                 rsdate=sig->timestamp;
-               }
-             /* If it's not a subkey sig or rev, then it shouldn't be
-                 here so ignore it. */
-           }
-         else
-           break;
-       }
-
-      /* We now know the most recent binding sig and revocation sig
-         (if any).  If the binding sig is more recent than the
-         revocation sig, strip everything but the binding sig.  If the
-         revocation sig is more recent than the binding sig, strip
-         everything but the binding sig and the revocation sig. */
-
-      if(bsdate>=rsdate)
-       {
-         rsnode=NULL;
-         rsdate=0;
-       }
-
-     for(node=sknode;node;node=node->next)
-       {
-         if(node->pkt->pkttype==PKT_SIGNATURE)
-           {
-             PKT_signature *sig=node->pkt->pkt.signature;
-
-             if(IS_SUBKEY_SIG(sig) && node!=bsnode)
-               {
-                 delete_kbnode(node);
-                 removed++;
-               }
-             else if(IS_SUBKEY_REV(sig) && node!=rsnode)
-               {
-                 delete_kbnode(node);
-                 removed++;
-               }
-           }
-         else
-           break;
-       }
-    }
-
-  if(removed)
-    {
-      log_info(_("key %08lX: removed multiple subkey binding\n"),
-              (ulong)keyid[1]);
-      commit_kbnode(&keyblock);
-    }
-
-  return removed;
+  byte array[MAX_FINGERPRINT_LEN], *s;
+  char buf[MAX_FINGERPRINT_LEN*2+30], *p;
+  size_t i, n;
+
+  sprintf (buf, "%u ", reason);
+  p = buf + strlen (buf);
+
+  if (pk)
+    fingerprint_from_pk (pk, array, &n);
+  else
+    fingerprint_from_sk (sk, array, &n);
+  s = array;
+  for (i=0; i < n ; i++, s++, p += 2)
+    sprintf (p, "%02X", *s);
+
+  write_status_text (STATUS_IMPORT_OK, buf);
 }
 
+void
+print_import_check (PKT_public_key * pk, PKT_user_id * id)
+{
+    char * buf;
+    byte fpr[24];
+    u32 keyid[2];
+    size_t i, pos = 0, n;
+
+    buf = m_alloc (17+41+id->len+32);
+    keyid_from_pk (pk, keyid);
+    sprintf (buf, "%08X%08X ", keyid[0], keyid[1]);
+    pos = 17;
+    fingerprint_from_pk (pk, fpr, &n);
+    for (i = 0; i < n; i++, pos += 2)
+        sprintf (buf+pos, "%02X", fpr[i]);
+    strcat (buf, " ");
+    pos += 1;
+    strcat (buf, id->name);
+    write_status_text (STATUS_IMPORT_CHECK, buf);
+    m_free (buf);
+}
 
 /****************
  * Try to import one keyblock. Return an error only in serious cases, but
@@ -606,7 +538,7 @@ clean_subkeys(KBNODE keyblock,u32 *keyid)
  * 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 int options )
 {
     PKT_public_key *pk;
@@ -617,6 +549,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     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 );
@@ -627,7 +560,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
-    if( opt.verbose ) {
+    if( opt.verbose && !opt.interactive ) {
        log_info( "pub  %4u%c/%08lX %s   ",
                  nbits_from_pk( pk ),
                  pubkey_letter( pk->pubkey_algo ),
@@ -641,14 +574,27 @@ import_one( const char *fname, KBNODE keyblock, int fast,
        log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]);
        return 0;
     }
+    
+    if (opt.interactive) {
+        if(is_status_enabled())
+         print_import_check (pk, uidnode->pkt->pkt.user_id);
+       merge_keys_and_selfsig (keyblock);
+        tty_printf ("\n");
+        show_basic_key_info (keyblock);
+        tty_printf ("\n");
+        if (!cpr_get_answer_is_yes ("import.okay",
+                                    "Do you want to import this key? (y/N) "))
+            return 0;
+    }
 
     clear_kbnode_flags( keyblock );
 
-    if((options&IMPORT_REPAIR_HKP_SUBKEY_BUG) && fix_hkp_corruption(keyblock))
-      log_info(_("key %08lX: HKP subkey corruption repaired\n"),
+    if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock)
+       && opt.verbose)
+      log_info(_("key %08lX: PKS subkey corruption repaired\n"),
               (ulong)keyid[1]);
 
-    rc = chk_self_sigs( fname, keyblock , pk, keyid );
+    rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self );
     if( rc )
        return rc== -1? 0:rc;
 
@@ -666,29 +612,27 @@ import_one( const char *fname, KBNODE keyblock, int fast,
          }
 
     if( !delete_inv_parts( fname, keyblock, keyid, options ) ) {
-       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"));
-       }
+        log_error( _("key %08lX: no valid user IDs\n"), (ulong)keyid[1]);
+       if( !opt.quiet )
+         log_info(_("this may be caused by a missing self-signature\n"));
        stats->no_user_id++;
        return 0;
     }
 
     /* do we have this key already in one of our pubrings ? */
     pk_orig = m_alloc_clear( sizeof *pk_orig );
-    rc = get_pubkey( pk_orig, keyid );
+    rc = get_pubkey_fast ( pk_orig, keyid );
     if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) {
        log_error( _("key %08lX: public key not found: %s\n"),
                                (ulong)keyid[1], g10_errstr(rc));
     }
-    else if ( rc && opt.merge_only ) {
+    else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) )
+      {
        if( opt.verbose )
-           log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] );
+         log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] );
        rc = 0;
-       fast = 1; /* so that we don't get into the trustdb update */
        stats->skipped_new_keys++;
-    }
+      }
     else if( rc ) { /* insert this key */
         KEYDB_HANDLE hd = keydb_new (0);
 
@@ -700,18 +644,26 @@ import_one( const char *fname, KBNODE keyblock, int fast,
        }
        if( opt.verbose > 1 )
            log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
-       clean_subkeys(keyblock,keyid);
        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
-          revalidation_mark ();
+         {
+           /* 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 ) {
-           char *p=get_user_id_native(keyid);
+           char *p=get_user_id_printable (keyid);
            log_info( _("key %08lX: public key \"%s\" imported\n"),
                      (ulong)keyid[1],p);
            m_free(p);
@@ -720,6 +672,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
            char *us = get_long_user_id_string( keyid );
            write_status_text( STATUS_IMPORTED, us );
            m_free(us);
+            print_import_ok (pk,NULL, 1);
        }
        stats->imported++;
        if( is_RSA( pk->pubkey_algo ) )
@@ -777,17 +730,16 @@ import_one( const char *fname, KBNODE keyblock, int fast,
        if( n_uids || n_sigs || n_subk ) {
            mod_key = 1;
            /* keyblock_orig has been updated; write */
-           n_sigs-=clean_subkeys(keyblock_orig,keyid);
            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
+           else if(non_self)
              revalidation_mark ();
 
            /* we are ready */
            if( !opt.quiet ) {
-               char *p=get_user_id_native(keyid);
+               char *p=get_user_id_printable(keyid);
                if( n_uids == 1 )
                    log_info( _("key %08lX: \"%s\" 1 new user ID\n"),
                                             (ulong)keyid[1], p);
@@ -812,10 +764,17 @@ import_one( const char *fname, KBNODE keyblock, int fast,
            stats->n_uids +=n_uids;
            stats->n_sigs +=n_sigs;
            stats->n_subk +=n_subk;
+
+            if (is_status_enabled ()) 
+                 print_import_ok (pk, NULL,
+                                  ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
        }
        else {
+             if (is_status_enabled ()) 
+                  print_import_ok (pk, NULL, 0);
+       
            if( !opt.quiet ) {
-               char *p=get_user_id_native(keyid);
+               char *p=get_user_id_printable(keyid);
                log_info( _("key %08lX: \"%s\" not changed\n"),
                          (ulong)keyid[1],p);
                m_free(p);
@@ -834,6 +793,65 @@ import_one( const char *fname, KBNODE keyblock, int fast,
     return rc;
 }
 
+/* Walk a secret keyblock and produce a public keyblock out of it. */
+static KBNODE
+sec_to_pub_keyblock(KBNODE sec_keyblock)
+{
+  KBNODE secnode,pub_keyblock=NULL,ctx=NULL;
+
+  while((secnode=walk_kbnode(sec_keyblock,&ctx,0)))
+    {
+      KBNODE pubnode;
+
+      if(secnode->pkt->pkttype==PKT_SECRET_KEY ||
+        secnode->pkt->pkttype==PKT_SECRET_SUBKEY)
+       {
+         /* Make a public key.  We only need to convert enough to
+            write the keyblock out. */
+
+         PKT_secret_key *sk=secnode->pkt->pkt.secret_key;
+         PACKET *pkt=m_alloc_clear(sizeof(PACKET));
+         PKT_public_key *pk=m_alloc_clear(sizeof(PKT_public_key));
+         int n;
+
+         if(secnode->pkt->pkttype==PKT_SECRET_KEY)
+           pkt->pkttype=PKT_PUBLIC_KEY;
+         else
+           pkt->pkttype=PKT_PUBLIC_SUBKEY;
+
+         pkt->pkt.public_key=pk;
+
+         pk->version=sk->version;
+         pk->timestamp=sk->timestamp;
+         pk->expiredate=sk->expiredate;
+         pk->pubkey_algo=sk->pubkey_algo;
+
+         n=pubkey_get_npkey(pk->pubkey_algo);
+         if(n==0)
+           pk->pkey[0]=mpi_copy(sk->skey[0]);
+         else
+           {
+             int i;
+
+             for(i=0;i<n;i++)
+               pk->pkey[i]=mpi_copy(sk->skey[i]);
+           }
+
+         pubnode=new_kbnode(pkt);
+       }
+      else
+       {
+         pubnode=clone_kbnode(secnode);
+       }
+
+      if(pub_keyblock==NULL)
+       pub_keyblock=pubnode;
+      else
+       add_kbnode(pub_keyblock,pubnode);
+    }
+
+  return pub_keyblock;
+}
 
 /****************
  * Ditto for secret keys.  Handling is simpler than for public keys.
@@ -843,7 +861,7 @@ import_one( const char *fname, KBNODE keyblock, int fast,
  */
 static int
 import_secret_one( const char *fname, KBNODE keyblock, 
-                   struct stats_s *stats)
+                   struct stats_s *stats, unsigned int options)
 {
     PKT_secret_key *sk;
     KBNODE node, uidnode;
@@ -876,34 +894,60 @@ import_secret_one( const char *fname, KBNODE keyblock,
        return 0;
     }
 
+    if(sk->protect.algo>110)
+      {
+       log_error(_("key %08lX: secret key with invalid cipher %d "
+                   "- skipped\n"),(ulong)keyid[1],sk->protect.algo);
+       return 0;
+      }
+
     clear_kbnode_flags( keyblock );
 
     /* do we have this key already in one of our secrings ? */
     rc = seckey_available( keyid );
-    if( rc == G10ERR_NO_SECKEY && !opt.merge_only ) { /* 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 */
         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;
+         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) );
+         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]);
+         log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]);
        stats->secret_imported++;
-    }
+        if (is_status_enabled ()) 
+         print_import_ok (NULL, sk, 1|16);
+
+       if(options&IMPORT_SK2PK)
+         {
+           /* Try and make a public key out of this. */
+
+           KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
+           import_one(fname,pub_keyblock,stats,opt.import_options);
+           release_kbnode(pub_keyblock);
+         }
+
+      }
     else if( !rc ) { /* we can't merge secret keys */
        log_error( _("key %08lX: already in secret keyring\n"),
                                                        (ulong)keyid[1]);
        stats->secret_dups++;
+        if (is_status_enabled ()) 
+             print_import_ok (NULL, sk, 16);
+
+       /* TODO: if we ever do merge secret keys, make sure to handle
+          the sec_to_pub_keyblock feature as well. */
     }
     else
        log_error( _("key %08lX: secret key not found: %s\n"),
@@ -935,8 +979,8 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     pk = m_alloc_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]);
+       log_error( _("key %08lX: no public key - "
+                    "can't apply revocation certificate\n"), (ulong)keyid[1]);
        rc = 0;
        goto leave;
     }
@@ -986,12 +1030,12 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
        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 */
-       }
+         }
     }
 
 
@@ -1006,12 +1050,19 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     keydb_release (hd); hd = NULL;
     /* we are ready */
     if( !opt.quiet ) {
-        char *p=get_user_id_native(keyid);
+        char *p=get_user_id_printable (keyid);
        log_info( _("key %08lX: \"%s\" revocation certificate imported\n"),
                                        (ulong)keyid[1],p);
        m_free(p);
     }
     stats->n_revoc++;
+
+    /* If the key we just revoked was ultimately trusted, remove its
+       ultimate trust.  This doesn't stop the user from putting the
+       ultimate trust back, but is a reasonable solution for now. */
+    if(get_ownertrust(pk)==TRUST_ULTIMATE)
+      clear_ownertrusts(pk);
+
     revalidation_mark ();
 
   leave:
@@ -1026,21 +1077,41 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
  * 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] ) {
+
+         /* This just caches the sigs for later use.  That way we
+            import a fully-cached key which speeds things up. */
+         if(!opt.no_sig_cache)
+           check_key_signature(keyblock,n,NULL);
+
            if( (sig->sig_class&~3) == 0x10 ) {
                KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
                if( !unode )  {
@@ -1054,48 +1125,114 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                  rc = check_key_signature( keyblock, n, NULL);
                  if( rc )
                    {
-                     char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+                     if( opt.verbose )
+                       {
+                         char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
                                      strlen(unode->pkt->pkt.user_id->name),0);
-                     log_info( rc == G10ERR_PUBKEY_ALGO ?
-                               _("key %08lX: unsupported public key "
-                                 "algorithm on user id \"%s\"\n"):
-                               _("key %08lX: invalid self-signature "
-                                 "on user id \"%s\"\n"),
-                               (ulong)keyid[1],p);
-                     m_free(p);
+                         log_info( rc == G10ERR_PUBKEY_ALGO ?
+                                   _("key %08lX: unsupported public key "
+                                     "algorithm on user id \"%s\"\n"):
+                                   _("key %08lX: invalid self-signature "
+                                     "on user id \"%s\"\n"),
+                                   (ulong)keyid[1],p);
+                         m_free(p);
+                       }
                    }
                  else
                    unode->flag |= 1; /* mark that signature checked */
                }
            }
            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(opt.verbose)
+                     log_info( _("key %08lX: no subkey for key binding\n"),
+                               (ulong)keyid[1]);
                    n->flag |= 4; /* delete this */
                }
                else {
-                 /* If it hasn't been marked valid yet, keep trying */
-                 if(!(knode->flag&1)) {
-                   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]);
+                 rc = check_key_signature( keyblock, n, NULL);
+                 if( rc ) {
+                   if(opt.verbose)
+                     log_info(rc == G10ERR_PUBKEY_ALGO ?
+                           _("key %08lX: unsupported public key algorithm\n"):
+                           _("key %08lX: invalid subkey binding\n"),
+                           (ulong)keyid[1]);
+                   n->flag|=4;
+                 }
+                 else {
+                   /* It's valid, so is it newer? */
+                   if(sig->timestamp>=bsdate) {
+                     knode->flag |= 1;  /* the subkey is valid */
+                     if(bsnode) {
+                       bsnode->flag|=4; /* Delete the last binding
+                                           sig since this one is
+                                           newer */
+                       if(opt.verbose)
+                         log_info(_("key %08lX: removed multiple subkey "
+                                    "binding\n"),(ulong)keyid[1]);
+                     }
+
+                     bsnode=n;
+                     bsdate=sig->timestamp;
+                   }
                    else
-                     knode->flag |= 1; /* mark that signature checked */
+                     n->flag|=4; /* older */
+                 }
+               }
+           }
+           else if( sig->sig_class == 0x28 ) {
+             /* We don't actually mark the subkey as revoked right
+                 now, so just check that the revocation sig is the
+                 most recent valid one.  Note that we don't care if
+                 the binding sig is newer than the revocation sig.
+                 See the comment in getkey.c:merge_selfsigs_subkey for
+                 more */
+               if( !knode ) {
+                   if(opt.verbose)
+                     log_info( _("key %08lX: no subkey for key revocation\n"),
+                               (ulong)keyid[1]);
+                   n->flag |= 4; /* delete this */
+               }
+               else {
+                 rc = check_key_signature( keyblock, n, NULL);
+                 if( rc ) {
+                     if(opt.verbose)
+                       log_info(rc == G10ERR_PUBKEY_ALGO ?
+                           _("key %08lX: unsupported public key algorithm\n"):
+                           _("key %08lX: invalid subkey revocation\n"),
+                           (ulong)keyid[1]);
+
+                     n->flag|=4;
+                 }
+                 else {
+                   /* It's valid, so is it newer? */
+                   if(sig->timestamp>=rsdate) {
+                     if(rsnode) {
+                       rsnode->flag|=4; /* Delete the last revocation
+                                           sig since this one is
+                                           newer */
+                       if(opt.verbose)
+                         log_info(_("key %08lX: removed multiple subkey "
+                                    "revocation\n"),(ulong)keyid[1]);
+                     }
+
+                     rsnode=n;
+                     rsdate=sig->timestamp;
+                   }
+                   else
+                     n->flag|=4; /* older */
                  }
                }
            }
        }
+       else
+         *non_self=1;
     }
+
     return 0;
 }
 
@@ -1167,18 +1304,18 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
             * 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 %08lX: non exportable signature "
+                         "(class %02x) - skipped\n"),
+                       (ulong)keyid[1], node->pkt->pkt.signature->sig_class );
            delete_kbnode( node );
        }
        else if( node->pkt->pkttype == PKT_SIGNATURE
                 && node->pkt->pkt.signature->sig_class == 0x20 )  {
            if( uid_seen ) {
-               log_error( _("key %08lX: revocation certificate "
-                                    "at wrong place - skipped\n"),
-                                   (ulong)keyid[1]);
+               if(opt.verbose)
+                 log_info( _("key %08lX: revocation certificate "
+                             "at wrong place - skipped\n"), (ulong)keyid[1]);
                delete_kbnode( node );
            }
            else {
@@ -1193,9 +1330,10 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
                  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));
+                     if(opt.verbose)
+                       log_info( _("key %08lX: invalid revocation "
+                                   "certificate: %s - skipped\n"),
+                                 (ulong)keyid[1], g10_errstr(rc));
                      delete_kbnode( node );
                    }
                }
@@ -1205,11 +1343,20 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
                 (node->pkt->pkt.signature->sig_class == 0x18 ||
                  node->pkt->pkt.signature->sig_class == 0x28) &&
                 !subkey_seen ) {
-           log_error( _("key %08lX: subkey signature "
-                        "in wrong place - skipped\n"),
-                      (ulong)keyid[1]);
+           if(opt.verbose)
+             log_info( _("key %08lX: subkey signature "
+                         "in wrong place - skipped\n"), (ulong)keyid[1]);
            delete_kbnode( node );
        }
+       else if( node->pkt->pkttype == PKT_SIGNATURE
+                && !IS_CERT(node->pkt->pkt.signature))
+         {
+           if(opt.verbose)
+             log_info(_("key %08lX: unexpected signature class (0x%02X) -"
+                        " skipped\n"),(ulong)keyid[1],
+                      node->pkt->pkt.signature->sig_class);
+           delete_kbnode(node);
+         }
        else if( (node->flag & 4) ) /* marked for deletion */
            delete_kbnode( node );
     }
@@ -1303,8 +1450,9 @@ collapse_uids( KBNODE *keyblock )
        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)
+      log_info(_("key %08lX: duplicated user ID detected - merged\n"),
+              (ulong)kid1);
 
     return 1;
 }
@@ -1354,15 +1502,15 @@ revocation_present(KBNODE keyblock)
                          itself? */
                      int rc;
 
-                     rc=get_pubkey_byfprint(NULL,sig->revkey[idx]->fpr,
-                                            MAX_FINGERPRINT_LEN);
+                     rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr,
+                                                   MAX_FINGERPRINT_LEN);
                      if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
                        {
                          /* No, so try and get it */
                          if(opt.keyserver_scheme &&
                             opt.keyserver_options.auto_key_retrieve)
                            {
-                             log_info(_("Warning: key %08lX may be revoked: "
+                             log_info(_("WARNING: key %08lX may be revoked: "
                                         "fetching revocation key %08lX\n"),
                                       (ulong)keyid_from_pk(pk,NULL),
                                       (ulong)keyid[1]);
@@ -1370,13 +1518,13 @@ revocation_present(KBNODE keyblock)
                                                      MAX_FINGERPRINT_LEN);
 
                              /* Do we have it now? */
-                             rc=get_pubkey_byfprint(NULL,
+                             rc=get_pubkey_byfprint_fast (NULL,
                                                     sig->revkey[idx]->fpr,
                                                     MAX_FINGERPRINT_LEN);
                            }
 
                          if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
-                           log_info(_("Warning: key %08lX may be revoked: "
+                           log_info(_("WARNING: key %08lX may be revoked: "
                                       "revocation key %08lX not present.\n"),
                                     (ulong)keyid_from_pk(pk,NULL),
                                     (ulong)keyid[1]);
@@ -1419,23 +1567,25 @@ 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 ) {
-               char *p=get_user_id_native(keyid);
                KBNODE n2 = clone_kbnode(node);
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
                 ++*n_sigs;
-               log_info(_("key %08lX: \"%s\" revocation certificate added\n"),
-                                        (ulong)keyid[1],p);
-               m_free(p);
+               if(!opt.quiet)
+                 {
+                   char *p=get_user_id_printable (keyid);
+                   log_info(_("key %08lX: \"%s\" revocation "
+                              "certificate added\n"), (ulong)keyid[1],p);
+                   m_free(p);
+                 }
            }
        }
     }
@@ -1464,8 +1614,9 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                insert_kbnode( keyblock_orig, n2, 0 );
                n2->flag |= 1;
                 ++*n_sigs;
-               log_info( _("key %08lX: direct key signature added\n"),
-                                        (ulong)keyid[1]);
+               if(!opt.quiet)
+                 log_info( _("key %08lX: direct key signature added\n"),
+                           (ulong)keyid[1]);
            }
        }
     }
@@ -1633,20 +1784,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