Add readcert command.
[gnupg.git] / g10 / import.c
index 04099de..88eb24e 100644 (file)
@@ -1,12 +1,12 @@
 /* import.c - import a key into our key storage.
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- *               2005 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ *               2007 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -15,9 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <errno.h>
 #include <assert.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "errors.h"
+#include "status.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "trustdb.h"
 #include "main.h"
@@ -55,15 +53,18 @@ struct stats_s {
     ulong secret_dups;
     ulong skipped_new_keys;
     ulong not_imported;
+    ulong n_sigs_cleaned;
+    ulong n_uids_cleaned;
 };
 
 
-static int import( IOBUF inp, const char* fname,
-                   struct stats_s *stats, unsigned int options );
+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 void revocation_present(KBNODE keyblock);
-static int import_one( const char *fname, KBNODE keyblock,
-                       struct stats_s *stats, unsigned int options);
+static int import_one(const char *fname, KBNODE keyblock,struct stats_s *stats,
+                     unsigned char **fpr,size_t *fpr_len,
+                     unsigned int options,int from_sk);
 static int import_secret_one( const char *fname, KBNODE keyblock,
                               struct stats_s *stats, unsigned int options);
 static int import_revoke_cert( const char *fname, KBNODE node,
@@ -89,17 +90,28 @@ parse_import_options(char *str,unsigned int *options,int noisy)
 {
   struct parse_options import_opts[]=
     {
-      {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL},
-      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL},
-      {"fast-import",IMPORT_FAST,NULL},
-      {"convert-sk-to-pk",IMPORT_SK2PK,NULL},
-      {"merge-only",IMPORT_MERGE_ONLY,NULL},
+      {"import-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},
-      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL},
+      {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL},
+      {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL},
       /* dummy */
-      {"import-unusable-sigs",0,NULL},
-      {NULL,0,NULL}
+      {"import-unusable-sigs",0,NULL,NULL},
+      {"import-clean-sigs",0,NULL,NULL},
+      {"import-clean-uids",0,NULL,NULL},
+      {NULL,0,NULL,NULL}
     };
 
   return parse_options(str,options,import_opts,noisy);
@@ -108,13 +120,13 @@ parse_import_options(char *str,unsigned int *options,int noisy)
 void *
 import_new_stats_handle (void)
 {
-    return m_alloc_clear ( sizeof (struct stats_s) );
+    return xmalloc_clear ( sizeof (struct stats_s) );
 }
 
 void
 import_release_stats_handle (void *p)
 {
-    m_free (p);
+    xfree (p);
 }
 
 /****************
@@ -150,7 +162,8 @@ import_release_stats_handle (void *p)
  */
 static int
 import_keys_internal( IOBUF inp, char **fnames, int nnames,
-                     void *stats_handle, unsigned int options )
+                     void *stats_handle, unsigned char **fpr, size_t *fpr_len,
+                     unsigned int options )
 {
     int i, rc = 0;
     struct stats_s *stats = stats_handle;
@@ -159,7 +172,7 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
         stats = import_new_stats_handle ();
 
     if (inp) {
-        rc = import( inp, "[stream]", stats, options);
+        rc = import( inp, "[stream]", stats, fpr, fpr_len, options);
     }
     else {
         if( !fnames && !nnames )
@@ -178,15 +191,16 @@ import_keys_internal( IOBUF inp, char **fnames, int nnames,
               }
            if( !inp2 )
                log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
-           else {
-               rc = import( inp2, fname, stats, options );
+           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) );
-           }
+                 log_error("import from `%s' failed: %s\n", fname,
+                           g10_errstr(rc) );
+             }
            if( !fname )
                break;
        }
@@ -212,34 +226,40 @@ void
 import_keys( char **fnames, int nnames,
             void *stats_handle, unsigned int options )
 {
-    import_keys_internal( NULL, fnames, nnames, stats_handle, options);
+  import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options);
 }
 
 int
-import_keys_stream( IOBUF inp, void *stats_handle, unsigned int options )
+import_keys_stream( IOBUF inp, void *stats_handle,
+                   unsigned char **fpr, size_t *fpr_len,unsigned int options )
 {
-    return import_keys_internal( inp, NULL, 0, stats_handle, options);
+  return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options);
 }
 
 static int
-import( IOBUF inp, const char* fname,
-       struct stats_s *stats, unsigned int options )
+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;
+    KBNODE keyblock = NULL;  /* Need to initialize because gcc can't
+                                grasp the return semantics of
+                                read_block. */
     int rc = 0;
 
     getkey_disable_caches();
 
     if( !opt.no_armor ) { /* armored reading is not disabled */
-       armor_filter_context_t *afx = m_alloc_clear( sizeof *afx );
+       armor_filter_context_t *afx;
+
+        afx = new_armor_context ();
        afx->only_keyblocks = 1;
-       iobuf_push_filter2( inp, armor_filter, afx, 1 );
+       push_armor_filter (afx, inp);
+        release_armor_context (afx);
     }
 
     while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
        if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
-           rc = import_one( fname, keyblock, stats, options );
+           rc = import_one( fname, keyblock, stats, fpr, fpr_len, options, 0);
        else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) 
                 rc = import_secret_one( fname, keyblock, stats, options );
        else if( keyblock->pkt->pkttype == PKT_SIGNATURE
@@ -280,9 +300,9 @@ import_print_stats (void *hd)
            log_info(_("          w/o user IDs: %lu\n"), stats->no_user_id );
        if( stats->imported || stats->imported_rsa ) {
            log_info(_("              imported: %lu"), stats->imported );
-           if( stats->imported_rsa )
-               fprintf(stderr, "  (RSA: %lu)", stats->imported_rsa );
-           putc('\n', stderr);
+           if (stats->imported_rsa)
+              log_printf ("  (RSA: %lu)", stats->imported_rsa );
+           log_printf ("\n");
        }
        if( stats->unchanged )
            log_info(_("             unchanged: %lu\n"), stats->unchanged );
@@ -302,6 +322,10 @@ import_print_stats (void *hd)
            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() ) {
@@ -347,7 +371,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 */
@@ -380,7 +404,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
              }
            else
              {
-               compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx );
+               compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx );
                pkt->pkt.compressed->buf = NULL;
                push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1);
              }
@@ -408,7 +432,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;
@@ -423,7 +447,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
     else
        *ret_root = root;
     free_packet( pkt );
-    m_free( pkt );
+    xfree( pkt );
     return rc;
 }
 
@@ -525,7 +549,7 @@ print_import_check (PKT_public_key * pk, PKT_user_id * id)
     u32 keyid[2];
     size_t i, pos = 0, n;
 
-    buf = m_alloc (17+41+id->len+32);
+    buf = xmalloc (17+41+id->len+32);
     keyid_from_pk (pk, keyid);
     sprintf (buf, "%08X%08X ", keyid[0], keyid[1]);
     pos = 17;
@@ -536,15 +560,14 @@ print_import_check (PKT_public_key * pk, PKT_user_id * id)
     pos += 1;
     strcat (buf, id->name);
     write_status_text (STATUS_IMPORT_CHECK, buf);
-    m_free (buf);
+    xfree (buf);
 }
 
 static void
 check_prefs_warning(PKT_public_key *pk)
 {
-  log_info(_("WARNING: key %s contains preferences for unavailable\n"),
-          keystr_from_pk(pk));
-  log_info(_("algorithms on these user IDs:\n"));
+  log_info(_("WARNING: key %s contains preferences for unavailable\n"
+             "algorithms on these user IDs:\n"), keystr_from_pk(pk));
 }
 
 static void
@@ -576,31 +599,37 @@ check_prefs(KBNODE keyblock)
 
              if(prefs->type==PREFTYPE_SYM)
                {
-                 if(check_cipher_algo(prefs->value))
+                 if (openpgp_cipher_test_algo (prefs->value))
                    {
-                     const char *algo=cipher_algo_to_string(prefs->value);
+                     const char *algo = 
+                        (openpgp_cipher_test_algo (prefs->value)
+                         ? num 
+                         : openpgp_cipher_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
                      log_info(_("         \"%s\": preference for cipher"
-                                " algorithm %s\n"),user,algo?algo:num);
+                                " algorithm %s\n"), user, algo);
                      problem=1;
                    }
                }
              else if(prefs->type==PREFTYPE_HASH)
                {
-                 if(check_digest_algo(prefs->value))
+                 if(openpgp_md_test_algo(prefs->value))
                    {
-                     const char *algo=digest_algo_to_string(prefs->value);
+                     const char *algo =
+                        (gcry_md_test_algo (prefs->value)
+                         ? num 
+                         : gcry_md_algo_name (prefs->value));
                      if(!problem)
                        check_prefs_warning(pk);
                      log_info(_("         \"%s\": preference for digest"
-                                " algorithm %s\n"),user,algo?algo:num);
+                                " algorithm %s\n"), user, algo);
                      problem=1;
                    }
                }
              else if(prefs->type==PREFTYPE_ZIP)
                {
-                 if(check_compress_algo(prefs->value))
+                 if(check_compress_algo (prefs->value))
                    {
                      const char *algo=compress_algo_to_string(prefs->value);
                      if(!problem)
@@ -612,7 +641,7 @@ check_prefs(KBNODE keyblock)
                }
            }
 
-         m_free(user);
+         xfree(user);
        }
     }
 
@@ -625,7 +654,7 @@ check_prefs(KBNODE keyblock)
 
       if(!opt.batch)
        {
-         STRLIST sl=NULL,locusr=NULL;
+         strlist_t sl=NULL,locusr=NULL;
          size_t fprlen=0;
          byte fpr[MAX_FINGERPRINT_LEN],*p;
          char username[(MAX_FINGERPRINT_LEN*2)+1];
@@ -653,11 +682,12 @@ check_prefs(KBNODE keyblock)
  * Try to import one keyblock. Return an error only in serious cases, but
  * never for an invalid keyblock.  It uses log_error to increase the
  * internal errorcount, so that invalid input can be detected by programs
- * which called g10.
+ * which called gpg.
  */
 static int
-import_one( const char *fname, KBNODE keyblock,
-            struct stats_s *stats, unsigned int options )
+import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
+           unsigned char **fpr,size_t *fpr_len,unsigned int options,
+           int from_sk )
 {
     PKT_public_key *pk;
     PKT_public_key *pk_orig;
@@ -667,6 +697,7 @@ import_one( const char *fname, KBNODE keyblock,
     int rc = 0;
     int new_key = 0;
     int mod_key = 0;
+    int same_key = 0;
     int non_self = 0;
 
     /* get the key and print some info about it */
@@ -675,6 +706,7 @@ import_one( const char *fname, KBNODE keyblock,
        BUG();
 
     pk = node->pkt->pkt.public_key;
+
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
@@ -684,12 +716,14 @@ import_one( const char *fname, KBNODE keyblock,
                  nbits_from_pk( pk ),
                  pubkey_letter( pk->pubkey_algo ),
                  keystr_from_pk(pk), datestr_from_pk(pk) );
-       if( uidnode )
-         print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+       if (uidnode)
+         print_utf8_string (log_get_stream (),
+                             uidnode->pkt->pkt.user_id->name,
                             uidnode->pkt->pkt.user_id->len );
-       putc('\n', stderr);
+       log_printf ("\n");
       }
 
+
     if( !uidnode )
       {
        log_error( _("key %s: no user ID\n"), keystr_from_pk(pk));
@@ -708,6 +742,15 @@ import_one( const char *fname, KBNODE keyblock,
             return 0;
     }
 
+    collapse_uids(&keyblock);
+
+    /* Clean the key that we're about to import, to cut down on things
+       that we have to clean later.  This has no practical impact on
+       the end result, but does result in less logging which might
+       confuse the user. */
+    if(options&IMPORT_CLEAN)
+      clean_key(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL);
+
     clear_kbnode_flags( keyblock );
 
     if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock)
@@ -729,7 +772,7 @@ import_one( const char *fname, KBNODE keyblock,
            node->flag |= 1;
            log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"),
                      keystr_from_pk(pk),user);
-           m_free(user);
+           xfree(user);
          }
 
     if( !delete_inv_parts( fname, keyblock, keyid, options ) ) {
@@ -741,7 +784,7 @@ import_one( const char *fname, KBNODE keyblock,
     }
 
     /* do we have this key already in one of our pubrings ? */
-    pk_orig = m_alloc_clear( sizeof *pk_orig );
+    pk_orig = xmalloc_clear( sizeof *pk_orig );
     rc = get_pubkey_fast ( pk_orig, keyid );
     if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY )
       {
@@ -767,8 +810,6 @@ import_one( const char *fname, KBNODE keyblock,
        if( opt.verbose > 1 )
            log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
 
-       collapse_uids(&keyblock);
-
        rc = keydb_insert_keyblock (hd, keyblock );
         if (rc)
           log_error (_("error writing keyring `%s': %s\n"),
@@ -792,13 +833,13 @@ import_one( const char *fname, KBNODE keyblock,
            char *p=get_user_id_native (keyid);
            log_info( _("key %s: public key \"%s\" imported\n"),
                      keystr(keyid),p);
-           m_free(p);
+           xfree(p);
          }
        if( is_status_enabled() )
          {
            char *us = get_long_user_id_string( keyid );
            write_status_text( STATUS_IMPORTED, us );
-           m_free(us);
+           xfree(us);
             print_import_ok (pk,NULL, 1);
          }
        stats->imported++;
@@ -808,7 +849,7 @@ import_one( const char *fname, KBNODE keyblock,
     }
     else { /* merge */
         KEYDB_HANDLE hd;
-       int n_uids, n_sigs, n_subk;
+       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 */
@@ -845,18 +886,23 @@ import_one( const char *fname, KBNODE keyblock,
            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 );
-       if( rc ) {
+                          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 */
            rc = keydb_update_keyblock (hd, keyblock_orig);
@@ -888,12 +934,26 @@ import_one( const char *fname, KBNODE keyblock,
                else if( n_subk )
                  log_info( _("key %s: \"%s\" %d new subkeys\n"),
                            keystr(keyid), p, n_subk );
-               m_free(p);
+               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,
@@ -901,14 +961,15 @@ import_one( const char *fname, KBNODE keyblock,
        }
        else
          {
-           if (is_status_enabled ()) 
+            same_key = 1;
+            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);
-               m_free(p);
+               xfree(p);
              }
 
            stats->unchanged++;
@@ -918,6 +979,33 @@ import_one( const char *fname, KBNODE keyblock,
     }
 
   leave:
+    if (mod_key || new_key || same_key)
+      {
+       /* A little explanation for this: we fill in the fingerprint
+          when importing keys as it can be useful to know the
+          fingerprint in certain keyserver-related cases (a keyserver
+          asked for a particular name, but the key doesn't have that
+          name).  However, in cases where we're importing more than
+          one key at a time, we cannot know which key to fingerprint.
+          In these cases, rather than guessing, we do not
+          fingerprinting at all, and we must hope the user ID on the
+          keys are useful.  Note that we need to do this for new
+          keys, merged keys and even for unchanged keys.  This is
+          required because for example the --auto-key-locate feature
+          may import an already imported key and needs to know the
+          fingerprint of the key in all cases.  */
+       if (fpr)
+         {
+           xfree (*fpr);
+            /* Note that we need to compare against 0 here because
+               COUNT gets only incremented after returning form this
+               function.  */
+           if (stats->count == 0)
+             *fpr = fingerprint_from_pk (pk, NULL, fpr_len);
+           else
+             *fpr = NULL;
+         }
+      }
 
     /* Now that the key is definitely incorporated into the keydb, we
        need to check if a designated revocation is present or if the
@@ -926,13 +1014,13 @@ import_one( const char *fname, KBNODE keyblock,
     if(mod_key)
       {
        revocation_present(keyblock_orig);
-       if(seckey_available(keyid)==0)
+       if(!from_sk && seckey_available(keyid)==0)
          check_prefs(keyblock_orig);
       }
     else if(new_key)
       {
        revocation_present(keyblock);
-       if(seckey_available(keyid)==0)
+       if(!from_sk && seckey_available(keyid)==0)
          check_prefs(keyblock);
       }
 
@@ -959,8 +1047,8 @@ sec_to_pub_keyblock(KBNODE sec_keyblock)
             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));
+         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)
@@ -1040,7 +1128,7 @@ import_secret_one( const char *fname, KBNODE keyblock,
        if( uidnode )
          print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
                             uidnode->pkt->pkt.user_id->len );
-       putc('\n', stderr);
+       log_printf ("\n");
       }
     stats->secret_read++;
 
@@ -1103,7 +1191,8 @@ import_secret_one( const char *fname, KBNODE keyblock,
            KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
            if(pub_keyblock)
              {
-               import_one(fname,pub_keyblock,stats,opt.import_options);
+               import_one(fname,pub_keyblock,stats,
+                          NULL,NULL,opt.import_options,1);
                release_kbnode(pub_keyblock);
              }
          }
@@ -1149,6 +1238,8 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     u32 keyid[2];
     int rc = 0;
 
+    (void)fname;
+
     assert( !node->next );
     assert( node->pkt->pkttype == PKT_SIGNATURE );
     assert( node->pkt->pkt.signature->sig_class == 0x20 );
@@ -1156,7 +1247,7 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
     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 )
       {
@@ -1237,7 +1328,7 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
         char *p=get_user_id_native (keyid);
        log_info( _("key %s: \"%s\" revocation certificate imported\n"),
                  keystr(keyid),p);
-       m_free(p);
+       xfree(p);
       }
     stats->n_revoc++;
 
@@ -1276,6 +1367,9 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
     u32 bsdate=0,rsdate=0;
     KBNODE bsnode=NULL,rsnode=NULL;
 
+    (void)fname;
+    (void)pk;
+
     for( n=keyblock; (n = find_next_kbnode(n, 0)); ) {
       if(n->pkt->pkttype==PKT_PUBLIC_SUBKEY)
        {
@@ -1291,12 +1385,13 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
        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);
+           /* 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 ) {
+           if( IS_UID_SIG(sig) || IS_UID_REV(sig) )
+             {
                KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
                if( !unode )
                  {
@@ -1320,13 +1415,13 @@ chk_self_sigs( const char *fname, KBNODE keyblock,
                                    _("key %s: invalid self-signature "
                                      "on user ID \"%s\"\n"),
                                    keystr(keyid),p);
-                         m_free(p);
+                         xfree(p);
                        }
                    }
                  else
                    unode->flag |= 1; /* mark that signature checked */
                }
-           }
+             }
            else if( sig->sig_class == 0x18 ) {
              /* Note that this works based solely on the timestamps
                 like the rest of gpg.  If the standard gets
@@ -1447,6 +1542,8 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
     KBNODE node;
     int nvalid=0, uid_seen=0, subkey_seen=0;
 
+    (void)fname;
+
     for(node=keyblock->next; node; node = node->next ) {
        if( node->pkt->pkttype == PKT_USER_ID ) {
            uid_seen = 1;
@@ -1457,7 +1554,7 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
                                           node->pkt->pkt.user_id->len,0);
                    log_info( _("key %s: skipped user ID \"%s\"\n"),
                              keystr(keyid),p);
-                   m_free(p);
+                   xfree(p);
                  }
                delete_kbnode( node ); /* the user-id */
                /* and all following packets up to the next user-id */
@@ -1489,9 +1586,9 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
            else
              subkey_seen = 1;
        }
-       else ifnode->pkt->pkttype == PKT_SIGNATURE
-                && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo)
-                && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
+       else if (node->pkt->pkttype == PKT_SIGNATURE
+                && openpgp_pk_test_algo (node->pkt->pkt.signature->pubkey_algo)
+               && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
            delete_kbnode( node ); /* build_packet() can't handle this */
        else if( node->pkt->pkttype == PKT_SIGNATURE &&
                 !node->pkt->pkt.signature->flags.exportable &&
@@ -1572,90 +1669,119 @@ delete_inv_parts( const char *fname, KBNODE keyblock,
  * It may happen that the imported keyblock has duplicated user IDs.
  * We check this here and collapse those user IDs together with their
  * sigs into one.
- * Returns: True if the keyblock hash changed.
+ * Returns: True if the keyblock has changed.
  */
 int
 collapse_uids( KBNODE *keyblock )
 {
-    KBNODE n, n2;
-    int in_uid;
-    int any=0;
+  KBNODE uid1;
+  int any=0;
 
-  restart:
-    for( n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype != PKT_USER_ID )
+  for(uid1=*keyblock;uid1;uid1=uid1->next)
+    {
+      KBNODE uid2;
+
+      if(is_deleted_kbnode(uid1))
+       continue;
+
+      if(uid1->pkt->pkttype!=PKT_USER_ID)
+       continue;
+
+      for(uid2=uid1->next;uid2;uid2=uid2->next)
+       {
+         if(is_deleted_kbnode(uid2))
            continue;
-       for( n2 = n->next; n2; n2 = n2->next ) {
-           if( n2->pkt->pkttype == PKT_USER_ID
-               && !cmp_user_ids( n->pkt->pkt.user_id,
-                                 n2->pkt->pkt.user_id ) ) {
-               /* found a duplicate */
-               any = 1;
-               if( !n2->next
-                   || n2->next->pkt->pkttype == PKT_USER_ID
-                   || n2->next->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                   || n2->next->pkt->pkttype == PKT_SECRET_SUBKEY  ) {
-                   /* no more signatures: delete the user ID
-                    * and start over */
-                   remove_kbnode( keyblock, n2 );
-               }
-               else {
-                   /* The simple approach: Move one signature and
-                    * then start over to delete the next one :-( */
-                   move_kbnode( keyblock, n2->next, n->next );
+
+         if(uid2->pkt->pkttype!=PKT_USER_ID)
+           continue;
+
+         if(cmp_user_ids(uid1->pkt->pkt.user_id,
+                         uid2->pkt->pkt.user_id)==0)
+           {
+             /* We have a duplicated uid */
+             KBNODE sig1,last;
+
+             any=1;
+
+             /* Now take uid2's signatures, and attach them to
+                uid1 */
+             for(last=uid2;last->next;last=last->next)
+               {
+                 if(is_deleted_kbnode(last))
+                   continue;
+
+                 if(last->next->pkt->pkttype==PKT_USER_ID
+                    || last->next->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                    || last->next->pkt->pkttype==PKT_SECRET_SUBKEY)
+                   break;
                }
-               goto restart;
-           }
-       }
-    }
-    if( !any )
-       return 0;
 
-  restart_sig:
-    /* now we may have duplicate signatures on one user ID: fix this */
-    for( in_uid = 0, n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype == PKT_USER_ID )
-           in_uid = 1;
-       else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                || n->pkt->pkttype == PKT_SECRET_SUBKEY )
-           in_uid = 0;
-       else if( in_uid ) {
-           n2 = n;
-           do {
-               KBNODE ncmp = NULL;
-               for( ; n2; n2 = n2->next ) {
-                   if(    n2->pkt->pkttype == PKT_USER_ID
-                       || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                       || n2->pkt->pkttype == PKT_SECRET_SUBKEY )
+             /* Snip out uid2 */
+             (find_prev_kbnode(*keyblock,uid2,0))->next=last->next;
+
+             /* Now put uid2 in place as part of uid1 */
+             last->next=uid1->next;
+             uid1->next=uid2;
+             delete_kbnode(uid2);
+
+             /* Now dedupe uid1 */
+             for(sig1=uid1->next;sig1;sig1=sig1->next)
+               {
+                 KBNODE sig2;
+
+                 if(is_deleted_kbnode(sig1))
+                   continue;
+
+                 if(sig1->pkt->pkttype==PKT_USER_ID
+                    || sig1->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                    || sig1->pkt->pkttype==PKT_SECRET_SUBKEY)
+                   break;
+
+                 if(sig1->pkt->pkttype!=PKT_SIGNATURE)
+                   continue;
+
+                 for(sig2=sig1->next,last=sig1;sig2;last=sig2,sig2=sig2->next)
+                   {
+                     if(is_deleted_kbnode(sig2))
+                       continue;
+
+                     if(sig2->pkt->pkttype==PKT_USER_ID
+                        || sig2->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                        || sig2->pkt->pkttype==PKT_SECRET_SUBKEY)
                        break;
-                   if( n2->pkt->pkttype != PKT_SIGNATURE )
-                       ;
-                   else if( !ncmp )
-                       ncmp = n2;
-                   else if( !cmp_signatures( ncmp->pkt->pkt.signature,
-                                               n2->pkt->pkt.signature )) {
-                       remove_kbnode( keyblock, n2 );
-                       goto restart_sig;
+
+                     if(sig2->pkt->pkttype!=PKT_SIGNATURE)
+                       continue;
+
+                     if(cmp_signatures(sig1->pkt->pkt.signature,
+                                       sig2->pkt->pkt.signature)==0)
+                       {
+                         /* We have a match, so delete the second
+                            signature */
+                         delete_kbnode(sig2);
+                         sig2=last;
+                       }
                    }
                }
-               n2 = ncmp? ncmp->next : NULL;
-           } while( n2 );
+           }
        }
     }
 
-    if(!opt.quiet)
-      {
-       const char *key="???";
+  commit_kbnode(keyblock);
 
-       if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
-         key=keystr_from_pk(n->pkt->pkt.public_key);
-       else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
-         key=keystr_from_sk(n->pkt->pkt.secret_key);
+  if(any && !opt.quiet)
+    {
+      const char *key="???";
 
-       log_info(_("key %s: duplicated user ID detected - merged\n"),key);
-      }
+      if( (uid1=find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
+       key=keystr_from_pk(uid1->pkt->pkt.public_key);
+      else if( (uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
+       key=keystr_from_sk(uid1->pkt->pkt.secret_key);
 
-    return 1;
+      log_info(_("key %s: duplicated user ID detected - merged\n"),key);
+    }
+
+  return any;
 }
 
 /* Check for a 0x20 revocation from a revocation key that is not
@@ -1707,7 +1833,7 @@ revocation_present(KBNODE keyblock)
                                                    MAX_FINGERPRINT_LEN);
                      if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
                        {
-                         char *tempkeystr=m_strdup(keystr_from_pk(pk));
+                         char *tempkeystr=xstrdup(keystr_from_pk(pk));
 
                          /* No, so try and get it */
                          if(opt.keyserver
@@ -1732,7 +1858,7 @@ revocation_present(KBNODE keyblock)
                                       " revocation key %s not present.\n"),
                                     tempkeystr,keystr(keyid));
 
-                         m_free(tempkeystr);
+                         xfree(tempkeystr);
                        }
                    }
                }
@@ -1789,7 +1915,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
                    char *p=get_user_id_native (keyid);
                    log_info(_("key %s: \"%s\" revocation"
                               " certificate added\n"), keystr(keyid),p);
-                   m_free(p);
+                   xfree(p);
                  }
            }
        }
@@ -1928,11 +2054,14 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
  * append the userid starting with NODE and all signatures to KEYBLOCK.
  */
 static int
-append_uidKBNODE keyblock, KBNODE node, int *n_sigs,
-                                         const char *fname, u32 *keyid )
+append_uid (KBNODE keyblock, KBNODE node, int *n_sigs,
+            const char *fname, u32 *keyid )
 {
     KBNODE n, n_where=NULL;
 
+    (void)fname;
+    (void)keyid;
+
     assert(node->pkt->pkttype == PKT_USER_ID );
 
     /* find the position */
@@ -1980,6 +2109,9 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
     KBNODE n, n2;
     int found=0;
 
+    (void)fname;
+    (void)keyid;
+
     assert(dst->pkt->pkttype == PKT_USER_ID );
     assert(src->pkt->pkttype == PKT_USER_ID );
 
@@ -2015,12 +2147,15 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
  * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY.
  */
 static int
-merge_keysigsKBNODE dst, KBNODE src, int *n_sigs,
-                                   const char *fname, u32 *keyid )
+merge_keysigs (KBNODE dst, KBNODE src, int *n_sigs,
+               const char *fname, u32 *keyid)
 {
     KBNODE n, n2;
     int found=0;
 
+    (void)fname;
+    (void)keyid;
+
     assert(   dst->pkt->pkttype == PKT_PUBLIC_SUBKEY
           || dst->pkt->pkttype == PKT_SECRET_SUBKEY );
 
@@ -2068,11 +2203,14 @@ merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
  * Mark all new and copied packets by setting flag bit 0.
  */
 static int
-append_keyKBNODE keyblock, KBNODE node, int *n_sigs,
-                                         const char *fname, u32 *keyid )
+append_key (KBNODE keyblock, KBNODE node, int *n_sigs,
+            const char *fname, u32 *keyid)
 {
     KBNODE n;
 
+    (void)fname;
+    (void)keyid;
+
     assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
           || node->pkt->pkttype == PKT_SECRET_SUBKEY );
 
@@ -2114,8 +2252,8 @@ pub_to_sec_keyblock (KBNODE pub_keyblock)
          /* 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 = m_alloc_clear (sizeof *pkt);
-         PKT_secret_key *sk = m_alloc_clear (sizeof *sk);
+         PACKET *pkt = xmalloc_clear (sizeof *pkt);
+         PKT_secret_key *sk = xmalloc_clear (sizeof *sk);
           int i, n;
           
           if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY)
@@ -2266,7 +2404,8 @@ auto_create_card_key_stub ( const char *serialnostr,
     size_t an;
 
     fingerprint_from_pk (pk, afp, &an);
-    memset (afp, 0, MAX_FINGERPRINT_LEN);
+    if (an < MAX_FINGERPRINT_LEN)
+      memset (afp+an, 0, MAX_FINGERPRINT_LEN-an);
     rc = keydb_search_fpr (hd, afp);
   }
 
@@ -2321,4 +2460,3 @@ auto_create_card_key_stub ( const char *serialnostr,
   keydb_release (hd);
   return rc;
 }
-