import_one (ctrl_t ctrl,
            const char *fname, KBNODE keyblock, struct stats_s *stats,
	    unsigned char **fpr,size_t *fpr_len,unsigned int options,
	    int from_sk )
{
    PKT_public_key *pk;
    PKT_public_key *pk_orig;
    KBNODE node, uidnode;
    KBNODE keyblock_orig = NULL;
    u32 keyid[2];
    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 */
    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
    if( !node )
	BUG();

    pk = node->pkt->pkt.public_key;

    keyid_from_pk( pk, keyid );
    uidnode = find_next_kbnode( keyblock, PKT_USER_ID );

    if( opt.verbose && !opt.interactive )
      {
	log_info( "pub  %4u%c/%s %s  ",
		  nbits_from_pk( pk ),
		  pubkey_letter( pk->pubkey_algo ),
		  keystr_from_pk(pk), datestr_from_pk(pk) );
	if (uidnode)
	  print_utf8_buffer (log_get_stream (),
                             uidnode->pkt->pkt.user_id->name,
			     uidnode->pkt->pkt.user_id->len );
	log_printf ("\n");
      }


    if( !uidnode )
      {
	log_error( _("key %s: no user ID\n"), keystr_from_pk(pk));
	return 0;
      }

    if (opt.interactive) {
        if(is_status_enabled())
	  print_import_check (pk, uidnode->pkt->pkt.user_id);
	merge_keys_and_selfsig (keyblock);
        tty_printf ("\n");
        show_basic_key_info (keyblock);
        tty_printf ("\n");
        if (!cpr_get_answer_is_yes ("import.okay",
                                    "Do you want to import this key? (y/N) "))
            return 0;
    }

    collapse_uids(&keyblock);

    /* Clean the key that we're about to import, to cut down on things
       that we have to clean later.  This has no practical impact on
       the end result, but does result in less logging which might
       confuse the user. */
    if(options&IMPORT_CLEAN)
      clean_key(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL);

    clear_kbnode_flags( keyblock );

    if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock)
       && opt.verbose)
      log_info(_("key %s: PKS subkey corruption repaired\n"),
	       keystr_from_pk(pk));

    rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self );
    if( rc )
	return rc== -1? 0:rc;

    /* If we allow such a thing, mark unsigned uids as valid */
    if( opt.allow_non_selfsigned_uid )
      for( node=keyblock; node; node = node->next )
	if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) )
	  {
	    char *user=utf8_to_native(node->pkt->pkt.user_id->name,
				      node->pkt->pkt.user_id->len,0);
	    node->flag |= 1;
	    log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"),
		      keystr_from_pk(pk),user);
	    xfree(user);
	  }

    if( !delete_inv_parts( fname, keyblock, keyid, options ) ) {
        log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk));
	if( !opt.quiet )
	  log_info(_("this may be caused by a missing self-signature\n"));
	stats->no_user_id++;
	return 0;
    }

    /* do we have this key already in one of our pubrings ? */
    pk_orig = xmalloc_clear( sizeof *pk_orig );
    rc = get_pubkey_fast ( pk_orig, keyid );
    if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY )
      {
	log_error( _("key %s: public key not found: %s\n"),
		   keystr(keyid), g10_errstr(rc));
      }
    else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) )
      {
	if( opt.verbose )
	  log_info( _("key %s: new key - skipped\n"), keystr(keyid));
	rc = 0;
	stats->skipped_new_keys++;
      }
    else if( rc ) { /* insert this key */
        KEYDB_HANDLE hd = keydb_new ();

        rc = keydb_locate_writable (hd, NULL);
	if (rc) {
	    log_error (_("no writable keyring found: %s\n"), g10_errstr (rc));
            keydb_release (hd);
	    return G10ERR_GENERAL;
	}
	if( opt.verbose > 1 )
	    log_info (_("writing to '%s'\n"), keydb_get_resource_name (hd) );

	rc = keydb_insert_keyblock (hd, keyblock );
        if (rc)
	   log_error (_("error writing keyring '%s': %s\n"),
		       keydb_get_resource_name (hd), g10_errstr(rc));
	else
	  {
	    /* This should not be possible since we delete the
	       ownertrust when a key is deleted, but it can happen if
	       the keyring and trustdb are out of sync.  It can also
	       be made to happen with the trusted-key command. */

	    clear_ownertrusts (pk);
	    if(non_self)
	      revalidation_mark ();
	  }
        keydb_release (hd);

	/* we are ready */
	if( !opt.quiet )
	  {
	    char *p=get_user_id_native (keyid);
	    log_info( _("key %s: public key \"%s\" imported\n"),
		      keystr(keyid),p);
	    xfree(p);
	  }
	if( is_status_enabled() )
	  {
	    char *us = get_long_user_id_string( keyid );
	    write_status_text( STATUS_IMPORTED, us );
	    xfree(us);
            print_import_ok (pk, 1);
	  }
	stats->imported++;
	if( is_RSA( pk->pubkey_algo ) )
	    stats->imported_rsa++;
	new_key = 1;
    }
    else { /* merge */
        KEYDB_HANDLE hd;
	int n_uids, n_sigs, n_subk, n_sigs_cleaned, n_uids_cleaned;

	/* Compare the original against the new key; just to be sure nothing
	 * weird is going on */
	if( cmp_public_keys( pk_orig, pk ) )
	  {
	    log_error( _("key %s: doesn't match our copy\n"),keystr(keyid));
	    goto leave;
	  }

	/* now read the original keyblock */
        hd = keydb_new ();
        {
            byte afp[MAX_FINGERPRINT_LEN];
            size_t an;

            fingerprint_from_pk (pk_orig, afp, &an);
            while (an < MAX_FINGERPRINT_LEN)
                afp[an++] = 0;
            rc = keydb_search_fpr (hd, afp);
        }
	if( rc )
	  {
	    log_error (_("key %s: can't locate original keyblock: %s\n"),
		       keystr(keyid), g10_errstr(rc));
            keydb_release (hd);
	    goto leave;
	  }
	rc = keydb_get_keyblock (hd, &keyblock_orig );
	if (rc)
	  {
	    log_error (_("key %s: can't read original keyblock: %s\n"),
		       keystr(keyid), g10_errstr(rc));
            keydb_release (hd);
	    goto leave;
	  }

        /* Make sure the original direct key sigs are all sane.  */
        n_sigs_cleaned = fix_bad_direct_key_sigs (keyblock_orig, keyid);
        if (n_sigs_cleaned)
          commit_kbnode (&keyblock_orig);

	/* and try to merge the block */
	clear_kbnode_flags( keyblock_orig );
	clear_kbnode_flags( keyblock );
	n_uids = n_sigs = n_subk = n_uids_cleaned = 0;
	rc = merge_blocks( fname, keyblock_orig, keyblock,
			   keyid, &n_uids, &n_sigs, &n_subk );
	if( rc )
	  {
            keydb_release (hd);
	    goto leave;
	  }

	if(options&IMPORT_CLEAN)
	  clean_key(keyblock_orig,opt.verbose,options&IMPORT_MINIMAL,
		    &n_uids_cleaned,&n_sigs_cleaned);

	if( n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) {
	    mod_key = 1;
	    /* keyblock_orig has been updated; write */
	    rc = keydb_update_keyblock (hd, keyblock_orig);
            if (rc)
		log_error (_("error writing keyring '%s': %s\n"),
			     keydb_get_resource_name (hd), g10_errstr(rc) );
	    else if(non_self)
	      revalidation_mark ();

	    /* we are ready */
	    if( !opt.quiet )
	      {
	        char *p=get_user_id_native(keyid);
		if( n_uids == 1 )
		  log_info( _("key %s: \"%s\" 1 new user ID\n"),
			   keystr(keyid),p);
		else if( n_uids )
		  log_info( _("key %s: \"%s\" %d new user IDs\n"),
			    keystr(keyid),p,n_uids);
		if( n_sigs == 1 )
		  log_info( _("key %s: \"%s\" 1 new signature\n"),
			    keystr(keyid), p);
		else if( n_sigs )
		  log_info( _("key %s: \"%s\" %d new signatures\n"),
			    keystr(keyid), p, n_sigs );
		if( n_subk == 1 )
		  log_info( _("key %s: \"%s\" 1 new subkey\n"),
			    keystr(keyid), p);
		else if( n_subk )
		  log_info( _("key %s: \"%s\" %d new subkeys\n"),
			    keystr(keyid), p, n_subk );
		if(n_sigs_cleaned==1)
		  log_info(_("key %s: \"%s\" %d signature cleaned\n"),
			   keystr(keyid),p,n_sigs_cleaned);
		else if(n_sigs_cleaned)
		  log_info(_("key %s: \"%s\" %d signatures cleaned\n"),
			   keystr(keyid),p,n_sigs_cleaned);
		if(n_uids_cleaned==1)
		  log_info(_("key %s: \"%s\" %d user ID cleaned\n"),
			   keystr(keyid),p,n_uids_cleaned);
		else if(n_uids_cleaned)
		  log_info(_("key %s: \"%s\" %d user IDs cleaned\n"),
			   keystr(keyid),p,n_uids_cleaned);
		xfree(p);
	      }

	    stats->n_uids +=n_uids;
	    stats->n_sigs +=n_sigs;
	    stats->n_subk +=n_subk;
	    stats->n_sigs_cleaned +=n_sigs_cleaned;
	    stats->n_uids_cleaned +=n_uids_cleaned;

            if (is_status_enabled ())
              print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
	}
	else
	  {
            same_key = 1;
            if (is_status_enabled ())
	      print_import_ok (pk, 0);

	    if( !opt.quiet )
	      {
		char *p=get_user_id_native(keyid);
		log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p);
		xfree(p);
	      }

	    stats->unchanged++;
	  }

        keydb_release (hd); hd = NULL;
    }

  leave:
    if (mod_key || new_key || same_key)
      {
	/* A little explanation for this: we fill in the fingerprint
	   when importing keys as it can be useful to know the
	   fingerprint in certain keyserver-related cases (a keyserver
	   asked for a particular name, but the key doesn't have that
	   name).  However, in cases where we're importing more than
	   one key at a time, we cannot know which key to fingerprint.
	   In these cases, rather than guessing, we do not
	   fingerprinting at all, and we must hope the user ID on the
	   keys are useful.  Note that we need to do this for new
	   keys, merged keys and even for unchanged keys.  This is
	   required because for example the --auto-key-locate feature
	   may import an already imported key and needs to know the
	   fingerprint of the key in all cases.  */
	if (fpr)
	  {
	    xfree (*fpr);
            /* Note that we need to compare against 0 here because
               COUNT gets only incremented after returning form this
               function.  */
	    if (stats->count == 0)
	      *fpr = fingerprint_from_pk (pk, NULL, fpr_len);
	    else
	      *fpr = NULL;
	  }
      }

    /* Now that the key is definitely incorporated into the keydb, we
       need to check if a designated revocation is present or if the
       prefs are not rational so we can warn the user. */

    if (mod_key)
      {
	revocation_present (ctrl, keyblock_orig);
	if (!from_sk && have_secret_key_with_kid (keyid))
	  check_prefs (ctrl, keyblock_orig);
      }
    else if (new_key)
      {
	revocation_present (ctrl, keyblock);
	if (!from_sk && have_secret_key_with_kid (keyid))
	  check_prefs (ctrl, keyblock);
      }

    release_kbnode( keyblock_orig );
    free_public_key( pk_orig );

    return rc;
}