Fix bug 1091.
[gnupg.git] / g10 / import.c
index 1818a28..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"
@@ -60,12 +58,13 @@ struct stats_s {
 };
 
 
-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,
@@ -91,20 +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-clean",IMPORT_CLEAN_SIGS|IMPORT_CLEAN_UIDS,NULL},
-      {"import-clean-sigs",IMPORT_CLEAN_SIGS,NULL},
-      {"import-clean-uids",IMPORT_CLEAN_UIDS,NULL},
+      {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL,
+       N_("import signatures that are marked as local-only")},
+      {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,
+       N_("repair damage from the pks keyserver during import")},
+      {"fast-import",IMPORT_FAST,NULL,
+       N_("do not update the trustdb after import")},
+      {"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);
@@ -113,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);
 }
 
 /****************
@@ -155,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;
@@ -164,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 )
@@ -183,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;
        }
@@ -217,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
@@ -285,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 );
@@ -356,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 */
@@ -389,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);
              }
@@ -417,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;
@@ -432,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;
 }
 
@@ -534,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;
@@ -545,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
@@ -585,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)
@@ -621,7 +641,7 @@ check_prefs(KBNODE keyblock)
                }
            }
 
-         m_free(user);
+         xfree(user);
        }
     }
 
@@ -634,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];
@@ -658,29 +678,16 @@ check_prefs(KBNODE keyblock)
     }
 }
 
-static int
-clean_sigs_from_all_uids(KBNODE keyblock)
-{
-  KBNODE uidnode;
-  int deleted=0;
-
-  for(uidnode=keyblock->next;uidnode;uidnode=uidnode->next)
-    if(uidnode->pkt->pkttype==PKT_USER_ID)
-      deleted+=clean_sigs_from_uid(keyblock,uidnode,opt.verbose);
-
-  return deleted;
-}
-
-
 /****************
  * Try to import one keyblock. Return an error only in serious cases, but
  * never for an invalid keyblock.  It uses log_error to increase the
  * internal errorcount, so that invalid input can be detected by programs
- * which called g10.
+ * 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;
@@ -690,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 */
@@ -698,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 );
 
@@ -707,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));
@@ -731,15 +742,14 @@ import_one( const char *fname, KBNODE keyblock,
             return 0;
     }
 
+    collapse_uids(&keyblock);
+
     /* Clean the key that we're about to import, to cut down on things
        that we have to clean later.  This has no practical impact on
        the end result, but does result in less logging which might
        confuse the user. */
-    if(options&IMPORT_CLEAN_SIGS)
-      clean_sigs_from_all_uids(keyblock);
-
-    if(options&IMPORT_CLEAN_UIDS)
-      clean_uids_from_key(keyblock,opt.verbose);
+    if(options&IMPORT_CLEAN)
+      clean_key(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL);
 
     clear_kbnode_flags( keyblock );
 
@@ -762,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 ) ) {
@@ -774,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 )
       {
@@ -800,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"),
@@ -825,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++;
@@ -878,7 +886,6 @@ 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 );
@@ -891,11 +898,9 @@ import_one( const char *fname, KBNODE keyblock,
            goto leave;
          }
 
-       if(options&IMPORT_CLEAN_SIGS)
-         n_sigs_cleaned=clean_sigs_from_all_uids(keyblock_orig);
-
-        if(options&IMPORT_CLEAN_UIDS)
-         n_uids_cleaned=clean_uids_from_key(keyblock_orig,opt.verbose);
+       if(options&IMPORT_CLEAN)
+         clean_key(keyblock_orig,opt.verbose,options&IMPORT_MINIMAL,
+                   &n_uids_cleaned,&n_sigs_cleaned);
 
        if( n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) {
            mod_key = 1;
@@ -941,7 +946,7 @@ import_one( const char *fname, KBNODE keyblock,
                else if(n_uids_cleaned)
                  log_info(_("key %s: \"%s\" %d user IDs cleaned\n"),
                           keystr(keyid),p,n_uids_cleaned);
-               m_free(p);
+               xfree(p);
              }
 
            stats->n_uids +=n_uids;
@@ -956,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++;
@@ -973,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
@@ -981,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);
       }
 
@@ -1014,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)
@@ -1095,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++;
 
@@ -1158,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);
              }
          }
@@ -1204,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 );
@@ -1211,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 )
       {
@@ -1292,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++;
 
@@ -1331,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)
        {
@@ -1346,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 )
                  {
@@ -1375,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
@@ -1502,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;
@@ -1512,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 */
@@ -1544,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 &&
@@ -1627,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;
+
+  for(uid1=*keyblock;uid1;uid1=uid1->next)
+    {
+      KBNODE uid2;
+
+      if(is_deleted_kbnode(uid1))
+       continue;
+
+      if(uid1->pkt->pkttype!=PKT_USER_ID)
+       continue;
 
-  restart:
-    for( n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype != PKT_USER_ID )
+      for(uid2=uid1->next;uid2;uid2=uid2->next)
+       {
+         if(is_deleted_kbnode(uid2))
            continue;
-       for( n2 = n->next; n2; n2 = n2->next ) {
-           if( n2->pkt->pkttype == PKT_USER_ID
-               && !cmp_user_ids( n->pkt->pkt.user_id,
-                                 n2->pkt->pkt.user_id ) ) {
-               /* found a duplicate */
-               any = 1;
-               if( !n2->next
-                   || n2->next->pkt->pkttype == PKT_USER_ID
-                   || n2->next->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                   || n2->next->pkt->pkttype == PKT_SECRET_SUBKEY  ) {
-                   /* no more signatures: delete the user ID
-                    * and start over */
-                   remove_kbnode( keyblock, n2 );
-               }
-               else {
-                   /* The simple approach: Move one signature and
-                    * then start over to delete the next one :-( */
-                   move_kbnode( keyblock, n2->next, n->next );
+
+         if(uid2->pkt->pkttype!=PKT_USER_ID)
+           continue;
+
+         if(cmp_user_ids(uid1->pkt->pkt.user_id,
+                         uid2->pkt->pkt.user_id)==0)
+           {
+             /* We have a duplicated uid */
+             KBNODE sig1,last;
+
+             any=1;
+
+             /* Now take uid2's signatures, and attach them to
+                uid1 */
+             for(last=uid2;last->next;last=last->next)
+               {
+                 if(is_deleted_kbnode(last))
+                   continue;
+
+                 if(last->next->pkt->pkttype==PKT_USER_ID
+                    || last->next->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                    || last->next->pkt->pkttype==PKT_SECRET_SUBKEY)
+                   break;
                }
-               goto restart;
-           }
-       }
-    }
-    if( !any )
-       return 0;
 
-  restart_sig:
-    /* now we may have duplicate signatures on one user ID: fix this */
-    for( in_uid = 0, n = *keyblock; n; n = n->next ) {
-       if( n->pkt->pkttype == PKT_USER_ID )
-           in_uid = 1;
-       else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                || n->pkt->pkttype == PKT_SECRET_SUBKEY )
-           in_uid = 0;
-       else if( in_uid ) {
-           n2 = n;
-           do {
-               KBNODE ncmp = NULL;
-               for( ; n2; n2 = n2->next ) {
-                   if(    n2->pkt->pkttype == PKT_USER_ID
-                       || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
-                       || n2->pkt->pkttype == PKT_SECRET_SUBKEY )
+             /* Snip out uid2 */
+             (find_prev_kbnode(*keyblock,uid2,0))->next=last->next;
+
+             /* Now put uid2 in place as part of uid1 */
+             last->next=uid1->next;
+             uid1->next=uid2;
+             delete_kbnode(uid2);
+
+             /* Now dedupe uid1 */
+             for(sig1=uid1->next;sig1;sig1=sig1->next)
+               {
+                 KBNODE sig2;
+
+                 if(is_deleted_kbnode(sig1))
+                   continue;
+
+                 if(sig1->pkt->pkttype==PKT_USER_ID
+                    || sig1->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                    || sig1->pkt->pkttype==PKT_SECRET_SUBKEY)
+                   break;
+
+                 if(sig1->pkt->pkttype!=PKT_SIGNATURE)
+                   continue;
+
+                 for(sig2=sig1->next,last=sig1;sig2;last=sig2,sig2=sig2->next)
+                   {
+                     if(is_deleted_kbnode(sig2))
+                       continue;
+
+                     if(sig2->pkt->pkttype==PKT_USER_ID
+                        || sig2->pkt->pkttype==PKT_PUBLIC_SUBKEY
+                        || sig2->pkt->pkttype==PKT_SECRET_SUBKEY)
                        break;
-                   if( n2->pkt->pkttype != PKT_SIGNATURE )
-                       ;
-                   else if( !ncmp )
-                       ncmp = n2;
-                   else if( !cmp_signatures( ncmp->pkt->pkt.signature,
-                                               n2->pkt->pkt.signature )) {
-                       remove_kbnode( keyblock, n2 );
-                       goto restart_sig;
+
+                     if(sig2->pkt->pkttype!=PKT_SIGNATURE)
+                       continue;
+
+                     if(cmp_signatures(sig1->pkt->pkt.signature,
+                                       sig2->pkt->pkt.signature)==0)
+                       {
+                         /* We have a match, so delete the second
+                            signature */
+                         delete_kbnode(sig2);
+                         sig2=last;
+                       }
                    }
                }
-               n2 = ncmp? ncmp->next : NULL;
-           } while( n2 );
+           }
        }
     }
 
-    if(!opt.quiet)
-      {
-       const char *key="???";
+  commit_kbnode(keyblock);
 
-       if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
-         key=keystr_from_pk(n->pkt->pkt.public_key);
-       else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
-         key=keystr_from_sk(n->pkt->pkt.secret_key);
+  if(any && !opt.quiet)
+    {
+      const char *key="???";
 
-       log_info(_("key %s: duplicated user ID detected - merged\n"),key);
-      }
+      if( (uid1=find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
+       key=keystr_from_pk(uid1->pkt->pkt.public_key);
+      else if( (uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
+       key=keystr_from_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
@@ -1762,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
@@ -1787,7 +1858,7 @@ revocation_present(KBNODE keyblock)
                                       " revocation key %s not present.\n"),
                                     tempkeystr,keystr(keyid));
 
-                         m_free(tempkeystr);
+                         xfree(tempkeystr);
                        }
                    }
                }
@@ -1844,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);
                  }
            }
        }
@@ -1983,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 */
@@ -2035,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 );
 
@@ -2070,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 );
 
@@ -2123,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 );
 
@@ -2169,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)
@@ -2321,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);
   }
 
@@ -2376,4 +2460,3 @@ auto_create_card_key_stub ( const char *serialnostr,
   keydb_release (hd);
   return rc;
 }
-