parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
                      const u32 *sigstatus, kbnode_t *r_keyblock)
{
  gpg_error_t err;
  PACKET *pkt;
  kbnode_t keyblock = NULL;
  kbnode_t node, *tail;
  int in_cert, save_mode;
  u32 n_sigs;
  int pk_count, uid_count;

  *r_keyblock = NULL;

  pkt = xtrymalloc (sizeof *pkt);
  if (!pkt)
    return gpg_error_from_syserror ();
  init_packet (pkt);
  save_mode = set_packet_list_mode (0);
  in_cert = 0;
  n_sigs = 0;
  tail = NULL;
  pk_count = uid_count = 0;
  while ((err = parse_packet (iobuf, pkt)) != -1)
    {
      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET)
        {
          free_packet (pkt);
          init_packet (pkt);
          continue;
	}
      if (err)
        {
          log_error ("parse_keyblock_image: read error: %s\n",
                     gpg_strerror (err));
          err = gpg_error (GPG_ERR_INV_KEYRING);
          break;
        }

      /* Filter allowed packets.  */
      switch (pkt->pkttype)
        {
        case PKT_PUBLIC_KEY:
        case PKT_PUBLIC_SUBKEY:
        case PKT_SECRET_KEY:
        case PKT_SECRET_SUBKEY:
        case PKT_USER_ID:
        case PKT_ATTRIBUTE:
        case PKT_SIGNATURE:
          break; /* Allowed per RFC.  */

        default:
          /* Note that can't allow ring trust packets here and some of
             the other GPG specific packets don't make sense either.  */
          log_error ("skipped packet of type %d in keybox\n",
                     (int)pkt->pkttype);
          free_packet(pkt);
          init_packet(pkt);
          continue;
        }

      /* Other sanity checks.  */
      if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY)
        {
          log_error ("parse_keyblock_image: first packet in a keybox blob "
                     "is not a public key packet\n");
          err = gpg_error (GPG_ERR_INV_KEYRING);
          break;
        }
      if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY
                      || pkt->pkttype == PKT_SECRET_KEY))
        {
          log_error ("parse_keyblock_image: "
                     "multiple keyblocks in a keybox blob\n");
          err = gpg_error (GPG_ERR_INV_KEYRING);
          break;
        }
      in_cert = 1;

      if (pkt->pkttype == PKT_SIGNATURE && sigstatus)
        {
          PKT_signature *sig = pkt->pkt.signature;

          n_sigs++;
          if (n_sigs > sigstatus[0])
            {
              log_error ("parse_keyblock_image: "
                         "more signatures than found in the meta data\n");
              err = gpg_error (GPG_ERR_INV_KEYRING);
              break;

            }
          if (sigstatus[n_sigs])
            {
              sig->flags.checked = 1;
              if (sigstatus[n_sigs] == 1 )
                ; /* missing key */
              else if (sigstatus[n_sigs] == 2 )
                ; /* bad signature */
              else if (sigstatus[n_sigs] < 0x10000000)
                ; /* bad flag */
              else
                {
                  sig->flags.valid = 1;
                  /* Fixme: Shall we set the expired flag here?  */
                }
            }
        }

      node = new_kbnode (pkt);

      switch (pkt->pkttype)
        {
        case PKT_PUBLIC_KEY:
        case PKT_PUBLIC_SUBKEY:
        case PKT_SECRET_KEY:
        case PKT_SECRET_SUBKEY:
          if (++pk_count == pk_no)
            node->flag |= 1;
          break;

        case PKT_USER_ID:
          if (++uid_count == uid_no)
            node->flag |= 2;
          break;

        default:
          break;
        }

      if (!keyblock)
        keyblock = node;
      else
        *tail = node;
      tail = &node->next;
      pkt = xtrymalloc (sizeof *pkt);
      if (!pkt)
        {
          err = gpg_error_from_syserror ();
          break;
        }
      init_packet (pkt);
    }
  set_packet_list_mode (save_mode);

  if (err == -1 && keyblock)
    err = 0; /* Got the entire keyblock.  */

  if (!err && sigstatus && n_sigs != sigstatus[0])
    {
      log_error ("parse_keyblock_image: signature count does not match\n");
      err = gpg_error (GPG_ERR_INV_KEYRING);
    }

  if (err)
    release_kbnode (keyblock);
  else
    *r_keyblock = keyblock;
  free_packet (pkt);
  xfree (pkt);
  return err;
}


new_offset_hash_table (void)
{
  struct off_item **tbl;

  tbl = xmalloc_clear (2048 * sizeof *tbl);
  return tbl;
}


dump_hex_line (int c, int *i)
{
  if (*i && !(*i % 8))
    {
      if (*i && !(*i % 24))
	es_fprintf (listfp, "\n%4d:", *i);
      else
	es_putc (' ', listfp);
    }
  if (c == -1)
    es_fprintf (listfp, " EOF");
  else
    es_fprintf (listfp, " %02x", c);
  ++*i;
}


print_p2r_setparameters (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
  if (msglen < 10)
    return;
  DEBUGOUT_1 ("  bProtocolNum ......: 0x%02x\n", msg[7]);
  print_pr_data (msg, msglen, 8);
}


is_secured_file (int fd)
{
#ifdef ENABLE_SELINUX_HACKS
  struct stat buf;
  struct secured_file_item *sf;

  if (fd == -1)
    return 0; /* No file descriptor so it can't be secured either.  */

  /* Note that we print out a error here and claim that a file is
     secure if something went wrong. */
  if (fstat (fd, &buf))
    {
      log_error (_("fstat(%d) failed in %s: %s\n"), fd,
                 "is_secured_file", strerror (errno));
      return 1;
    }
/*   log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */
/*              (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
  for (sf=secured_files; sf; sf = sf->next)
    {
      if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
        return 1; /* Yes.  */
    }
#else /*!ENABLE_SELINUX_HACKS*/
  (void)fd;
#endif /*!ENABLE_SELINUX_HACKS*/
  return 0; /* No. */
}


do_generate_keypair (struct para_data_s *para,
		     struct output_control_s *outctrl, int card)
{
  gpg_error_t err;
  KBNODE pub_root = NULL;
  const char *s;
  PKT_public_key *pri_psk = NULL;
  PKT_public_key *sub_psk = NULL;
  struct revocation_key *revkey;
  int did_sub = 0;
  u32 timestamp;
  char *cache_nonce = NULL;

  if (outctrl->dryrun)
    {
      log_info("dry-run mode - key generation skipped\n");
      return;
    }

  if ( outctrl->use_files )
    {
      if ( outctrl->pub.newfname )
        {
          iobuf_close(outctrl->pub.stream);
          outctrl->pub.stream = NULL;
          if (outctrl->pub.fname)
            iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE,
                         0, (char*)outctrl->pub.fname);
          xfree( outctrl->pub.fname );
          outctrl->pub.fname =  outctrl->pub.newfname;
          outctrl->pub.newfname = NULL;

          if (is_secured_filename (outctrl->pub.fname) )
            {
              outctrl->pub.stream = NULL;
              gpg_err_set_errno (EPERM);
            }
          else
            outctrl->pub.stream = iobuf_create (outctrl->pub.fname, 0);
          if (!outctrl->pub.stream)
            {
              log_error(_("can't create '%s': %s\n"), outctrl->pub.newfname,
                        strerror(errno) );
              return;
            }
          if (opt.armor)
            {
              outctrl->pub.afx->what = 1;
              push_armor_filter (outctrl->pub.afx, outctrl->pub.stream);
            }
        }
      assert( outctrl->pub.stream );
      if (opt.verbose)
        log_info (_("writing public key to '%s'\n"), outctrl->pub.fname );
    }

  /* We create the packets as a tree of kbnodes.  Because the
     structure we create is known in advance we simply generate a
     linked list.  The first packet is a dummy packet which we flag as
     deleted.  The very first packet must always be a KEY packet.  */

  start_tree (&pub_root);

  timestamp = get_parameter_u32 (para, pKEYCREATIONDATE);
  if (!timestamp)
    timestamp = make_timestamp ();

  /* Note that, depending on the backend (i.e. the used scdaemon
     version), the card key generation may update TIMESTAMP for each
     key.  Thus we need to pass TIMESTAMP to all signing function to
     make sure that the binding signature is done using the timestamp
     of the corresponding (sub)key and not that of the primary key.
     An alternative implementation could tell the signing function the
     node of the subkey but that is more work than just to pass the
     current timestamp.  */

  if (!card)
    err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ),
                     get_parameter_uint( para, pKEYLENGTH ),
                     get_parameter_value (para, pKEYCURVE),
                     pub_root,
                     timestamp,
                     get_parameter_u32( para, pKEYEXPIRE ), 0,
                     outctrl->keygen_flags,
                     get_parameter_passphrase (para),
                     &cache_nonce);
  else
    err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root,
                        &timestamp,
                        get_parameter_u32 (para, pKEYEXPIRE));

  /* Get the pointer to the generated public key packet.  */
  if (!err)
    {
      pri_psk = pub_root->next->pkt->pkt.public_key;
      assert (pri_psk);
    }

  if (!err && (revkey = get_parameter_revkey (para, pREVOKER)))
    err = write_direct_sig (pub_root, pri_psk, revkey, timestamp, cache_nonce);

  if (!err && (s = get_parameter_value (para, pUSERID)))
    {
      write_uid (pub_root, s );
      err = write_selfsigs (pub_root, pri_psk,
                            get_parameter_uint (para, pKEYUSAGE), timestamp,
                            cache_nonce);
    }

  /* Write the auth key to the card before the encryption key.  This
     is a partial workaround for a PGP bug (as of this writing, all
     versions including 8.1), that causes it to try and encrypt to
     the most recent subkey regardless of whether that subkey is
     actually an encryption type.  In this case, the auth key is an
     RSA key so it succeeds. */

  if (!err && card && get_parameter (para, pAUTHKEYTYPE))
    {
      err = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root,
                          &timestamp,
                          get_parameter_u32 (para, pKEYEXPIRE));
      if (!err)
        err = write_keybinding (pub_root, pri_psk, NULL,
                                PUBKEY_USAGE_AUTH, timestamp, cache_nonce);
    }

  if (!err && get_parameter (para, pSUBKEYTYPE))
    {
      sub_psk = NULL;
      if (!card)
        {
          err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL),
                           get_parameter_uint (para, pSUBKEYLENGTH),
                           get_parameter_value (para, pSUBKEYCURVE),
                           pub_root,
                           timestamp,
                           get_parameter_u32 (para, pSUBKEYEXPIRE), 1,
                           outctrl->keygen_flags,
                           get_parameter_passphrase (para),
                           &cache_nonce);
          /* Get the pointer to the generated public subkey packet.  */
          if (!err)
            {
              kbnode_t node;

              for (node = pub_root; node; node = node->next)
                if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
                  sub_psk = node->pkt->pkt.public_key;
              assert (sub_psk);
            }
        }
      else
        {
          if ((s = get_parameter_value (para, pCARDBACKUPKEY)))
            {
              /* A backup of the encryption key has been requested.
                 Generate the key in software and import it then to
                 the card.  Write a backup file. */
              err = gen_card_key_with_backup
                (PUBKEY_ALGO_RSA, 2, 0, pub_root, timestamp,
                 get_parameter_u32 (para, pKEYEXPIRE), para);
            }
          else
            {
              err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root,
                                  &timestamp,
                                  get_parameter_u32 (para, pKEYEXPIRE));
            }
        }

      if (!err)
        err = write_keybinding (pub_root, pri_psk, sub_psk,
                                get_parameter_uint (para, pSUBKEYUSAGE),
                                timestamp, cache_nonce);
      did_sub = 1;
    }

  if (!err && outctrl->use_files)  /* Direct write to specified files.  */
    {
      err = write_keyblock (outctrl->pub.stream, pub_root);
      if (err)
        log_error ("can't write public key: %s\n", gpg_strerror (err));
    }
  else if (!err) /* Write to the standard keyrings.  */
    {
      KEYDB_HANDLE pub_hd = keydb_new ();

      err = keydb_locate_writable (pub_hd, NULL);
      if (err)
        log_error (_("no writable public keyring found: %s\n"),
                   gpg_strerror (err));

      if (!err && opt.verbose)
        {
          log_info (_("writing public key to '%s'\n"),
                    keydb_get_resource_name (pub_hd));
        }

      if (!err)
        {
          err = keydb_insert_keyblock (pub_hd, pub_root);
          if (err)
            log_error (_("error writing public keyring '%s': %s\n"),
                       keydb_get_resource_name (pub_hd), gpg_strerror (err));
        }

      keydb_release (pub_hd);

      if (!err)
        {
          int no_enc_rsa;
          PKT_public_key *pk;

          no_enc_rsa = ((get_parameter_algo (para, pKEYTYPE, NULL)
                         == PUBKEY_ALGO_RSA)
                        && get_parameter_uint (para, pKEYUSAGE)
                        && !((get_parameter_uint (para, pKEYUSAGE)
                              & PUBKEY_USAGE_ENC)) );

          pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key;

          keyid_from_pk (pk, pk->main_keyid);
          register_trusted_keyid (pk->main_keyid);

          update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK)
                                  | TRUST_ULTIMATE ));

          gen_standard_revoke (pk, cache_nonce);

          if (!opt.batch)
            {
              tty_printf (_("public and secret key created and signed.\n") );
              tty_printf ("\n");
              list_keyblock (pub_root, 0, 1, 1, NULL);
            }

          if (!opt.batch
              && (get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA
                  || no_enc_rsa )
              && !get_parameter (para, pSUBKEYTYPE) )
            {
              tty_printf(_("Note that this key cannot be used for "
                           "encryption.  You may want to use\n"
                           "the command \"--edit-key\" to generate a "
                           "subkey for this purpose.\n") );
            }
        }
    }

  if (err)
    {
      if (opt.batch)
        log_error ("key generation failed: %s\n", gpg_strerror (err) );
      else
        tty_printf (_("Key generation failed: %s\n"), gpg_strerror (err) );
      write_status_error (card? "card_key_generate":"key_generate", err);
      print_status_key_not_created ( get_parameter_value (para, pHANDLE) );
    }
  else
    {
      PKT_public_key *pk = find_kbnode (pub_root,
                                        PKT_PUBLIC_KEY)->pkt->pkt.public_key;
      print_status_key_created (did_sub? 'B':'P', pk,
                                get_parameter_value (para, pHANDLE));
    }

  release_kbnode (pub_root);
  xfree (cache_nonce);
}


write_direct_sig (KBNODE root, PKT_public_key *psk,
                  struct revocation_key *revkey, u32 timestamp,
                  const char *cache_nonce)
{
  gpg_error_t err;
  PACKET *pkt;
  PKT_signature *sig;
  KBNODE node;
  PKT_public_key *pk;

  if (opt.verbose)
    log_info (_("writing direct signature\n"));

  /* Get the pk packet from the pub_tree. */
  node = find_kbnode (root, PKT_PUBLIC_KEY);
  if (!node)
    BUG ();
  pk = node->pkt->pkt.public_key;

  /* We have to cache the key, so that the verification of the
     signature creation is able to retrieve the public key.  */
  cache_public_key (pk);

  /* Make the signature.  */
  err = make_keysig_packet (&sig, pk, NULL,NULL, psk, 0x1F,
                            0, timestamp, 0,
                            keygen_add_revkey, revkey, cache_nonce);
  if (err)
    {
      log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err) );
      return err;
    }

  pkt = xmalloc_clear (sizeof *pkt);
  pkt->pkttype = PKT_SIGNATURE;
  pkt->pkt.signature = sig;
  add_kbnode (root, new_kbnode (pkt));
  return err;
}


open_db()
{
  TRUSTREC rec;

  assert( db_fd == -1 );

  if (!lockhandle )
    lockhandle = dotlock_create (db_name, 0);
  if (!lockhandle )
    log_fatal( _("can't create lock for '%s'\n"), db_name );
#ifdef __riscos__
  if (dotlock_take (lockhandle, -1) )
    log_fatal( _("can't lock '%s'\n"), db_name );
#endif /* __riscos__ */
#ifdef HAVE_W32CE_SYSTEM
  {
    DWORD prevrc = 0;
    wchar_t *wname = utf8_to_wchar (db_name);
    if (wname)
      {
        db_fd = (int)CreateFile (wname, GENERIC_READ|GENERIC_WRITE,
                                 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
                                 OPEN_EXISTING, 0, NULL);
        xfree (wname);
      }
    if (db_fd == -1)
      log_fatal ("can't open '%s': %d, %d\n", db_name,
                 (int)prevrc, (int)GetLastError ());
  }
#else /*!HAVE_W32CE_SYSTEM*/
  db_fd = open (db_name, O_RDWR | MY_O_BINARY );
  if (db_fd == -1 && (errno == EACCES
#ifdef EROFS
                      || errno == EROFS
#endif
                      )
      ) {
      /* Take care of read-only trustdbs.  */
      db_fd = open (db_name, O_RDONLY | MY_O_BINARY );
      if (db_fd != -1 && !opt.quiet)
          log_info (_("Note: trustdb not writable\n"));
  }
  if ( db_fd == -1 )
    log_fatal( _("can't open '%s': %s\n"), db_name, strerror(errno) );
#endif /*!HAVE_W32CE_SYSTEM*/
  register_secured_file (db_name);

  /* Read the version record. */
  if (tdbio_read_record (0, &rec, RECTYPE_VER ) )
    log_fatal( _("%s: invalid trustdb\n"), db_name );
}


send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
{
  char buffer[200];
  int n_bits;
  const char *curve_oid;

  assert (number >=0 && number < DIM(app->app_local->keyattr));

  if (app->app_local->keyattr[number].key_type == KEY_TYPE_RSA)
    snprintf (buffer, sizeof buffer, "%d 1 %u %u %d",
              number+1,
              app->app_local->keyattr[number].rsa.n_bits,
              app->app_local->keyattr[number].rsa.e_bits,
              app->app_local->keyattr[number].rsa.format);
  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_ECC)
    {
      get_ecc_key_parameters (app->app_local->keyattr[number].ecc.curve,
                              &n_bits, &curve_oid);
      snprintf (buffer, sizeof buffer, "%d %d %u %s",
                number+1, number==1? 18: 19, n_bits, curve_oid);
    }
  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_EDDSA)
    {
      get_ecc_key_parameters (app->app_local->keyattr[number].eddsa.curve,
                              &n_bits, &curve_oid);
      snprintf (buffer, sizeof buffer, "%d 22 %u %s",
                number+1, n_bits, curve_oid);
    }
  else
    snprintf (buffer, sizeof buffer, "0 0 UNKNOWN");

  send_status_direct (ctrl, keyword, buffer);
}


gen_rsa (int algo, unsigned int nbits, KBNODE pub_root,
         u32 timestamp, u32 expireval, int is_subkey,
         int keygen_flags, const char *passphrase, char **cache_nonce_addr)
{
  int err;
  char *keyparms;
  char nbitsstr[35];
  const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096);

  assert (is_RSA(algo));

  if (!nbits)
    nbits = DEFAULT_STD_KEYSIZE;

  if (nbits < 1024)
    {
      nbits = 2048;
      log_info (_("keysize invalid; using %u bits\n"), nbits );
    }
  else if (nbits > maxsize)
    {
      nbits = maxsize;
      log_info (_("keysize invalid; using %u bits\n"), nbits );
    }

  if ((nbits % 32))
    {
      nbits = ((nbits + 31) / 32) * 32;
      log_info (_("keysize rounded up to %u bits\n"), nbits );
    }

  snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits);
  keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))",
                           strlen (nbitsstr), nbitsstr,
                           ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
                            && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
                           "(transient-key)" : "" );
  if (!keyparms)
    err = gpg_error_from_syserror ();
  else
    {
      err = common_gen (keyparms, algo, "ne",
                        pub_root, timestamp, expireval, is_subkey,
                        keygen_flags, passphrase, cache_nonce_addr);
      xfree (keyparms);
    }

  return err;
}


print_p2r_iccclock (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
  if (msglen < 10)
    return;
  printf ("  bClockCommand .....: 0x%02x\n", msg[7]);
  print_pr_data (msg, msglen, 8);
}


revokestr_from_pk( PKT_public_key *pk )
{
  static char buffer[11+5];
  time_t atime;

  if(!pk->revoked.date)
    return _("never     ");
  atime=pk->revoked.date;
  return mk_datestr (buffer, atime);
}


tdbio_write_record( TRUSTREC *rec )
{
    byte buf[TRUST_RECORD_LEN], *p;
    int rc = 0;
    int i;
    ulong recnum = rec->recnum;

    if( db_fd == -1 )
	open_db();

    memset(buf, 0, TRUST_RECORD_LEN);
    p = buf;
    *p++ = rec->rectype; p++;
    switch( rec->rectype ) {
      case 0:  /* unused record */
	break;
      case RECTYPE_VER: /* version record */
	if( recnum )
	    BUG();
	memcpy(p-1, GPGEXT_GPG, 3 ); p += 2;
	*p++ = rec->r.ver.version;
	*p++ = rec->r.ver.marginals;
	*p++ = rec->r.ver.completes;
	*p++ = rec->r.ver.cert_depth;
	*p++ = rec->r.ver.trust_model;
	*p++ = rec->r.ver.min_cert_level;
	p += 2;
	ulongtobuf(p, rec->r.ver.created); p += 4;
	ulongtobuf(p, rec->r.ver.nextcheck); p += 4;
	p += 4;
	p += 4;
	ulongtobuf(p, rec->r.ver.firstfree ); p += 4;
	p += 4;
	ulongtobuf(p, rec->r.ver.trusthashtbl ); p += 4;
	break;

      case RECTYPE_FREE:
	ulongtobuf(p, rec->r.free.next); p += 4;
	break;

      case RECTYPE_HTBL:
	for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
	    ulongtobuf( p, rec->r.htbl.item[i]); p += 4;
	}
	break;

      case RECTYPE_HLST:
	ulongtobuf( p, rec->r.hlst.next); p += 4;
	for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
	    ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4;
	}
	break;

      case RECTYPE_TRUST:
	memcpy( p, rec->r.trust.fingerprint, 20); p += 20;
	*p++ = rec->r.trust.ownertrust;
	*p++ = rec->r.trust.depth;
	*p++ = rec->r.trust.min_ownertrust;
        p++;
	ulongtobuf( p, rec->r.trust.validlist); p += 4;
	break;

      case RECTYPE_VALID:
	memcpy( p, rec->r.valid.namehash, 20); p += 20;
	*p++ = rec->r.valid.validity;
	ulongtobuf( p, rec->r.valid.next); p += 4;
	*p++ = rec->r.valid.full_count;
	*p++ = rec->r.valid.marginal_count;
	break;

      default:
	BUG();
    }

    rc = put_record_into_cache( recnum, buf );
    if( rc )
	;
    else if( rec->rectype == RECTYPE_TRUST )
	rc = update_trusthashtbl( rec );

    return rc;
}


close_rapdu_reader (int slot)
{
  rapdu_release (reader_table[slot].rapdu.handle);
  reader_table[slot].used = 0;
  return 0;
}


do_create_from_keygrip (ctrl_t ctrl, int algo, const char *hexkeygrip,
                        kbnode_t pub_root, u32 timestamp, u32 expireval,
                        int is_subkey)
{
  int err;
  PACKET *pkt;
  PKT_public_key *pk;
  gcry_sexp_t s_key;
  const char *algoelem;

  if (hexkeygrip[0] == '&')
    hexkeygrip++;

  switch (algo)
    {
    case PUBKEY_ALGO_RSA:       algoelem = "ne"; break;
    case PUBKEY_ALGO_DSA:       algoelem = "pqgy"; break;
    case PUBKEY_ALGO_ELGAMAL_E: algoelem = "pgy"; break;
    case PUBKEY_ALGO_ECDH:
    case PUBKEY_ALGO_ECDSA:     algoelem = ""; break;
    case PUBKEY_ALGO_EDDSA:     algoelem = ""; break;
    default: return gpg_error (GPG_ERR_INTERNAL);
    }

  /* Ask the agent for the public key matching HEXKEYGRIP.  */
  {
    unsigned char *public;

    err = agent_readkey (ctrl, 0, hexkeygrip, &public);
    if (err)
      return err;
    err = gcry_sexp_sscan (&s_key, NULL,
                           public, gcry_sexp_canon_len (public, 0, NULL, NULL));
    xfree (public);
    if (err)
      return err;
  }

  /* Build a public key packet.  */
  pk = xtrycalloc (1, sizeof *pk);
  if (!pk)
    {
      err = gpg_error_from_syserror ();
      gcry_sexp_release (s_key);
      return err;
    }

  pk->timestamp = timestamp;
  pk->version = 4;
  if (expireval)
    pk->expiredate = pk->timestamp + expireval;
  pk->pubkey_algo = algo;

  if (algo == PUBKEY_ALGO_ECDSA
      || algo == PUBKEY_ALGO_EDDSA
      || algo == PUBKEY_ALGO_ECDH )
    err = ecckey_from_sexp (pk->pkey, s_key, algo);
  else
    err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
  if (err)
    {
      log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) );
      gcry_sexp_release (s_key);
      free_public_key (pk);
      return err;
    }
  gcry_sexp_release (s_key);

  pkt = xtrycalloc (1, sizeof *pkt);
  if (!pkt)
    {
      err = gpg_error_from_syserror ();
      free_public_key (pk);
      return err;
    }

  pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
  pkt->pkt.public_key = pk;
  add_kbnode (pub_root, new_kbnode (pkt));

  return 0;
}


print_string (FILE *fp, const byte *p, size_t n, int delim)
{
  for ( ; n; n--, p++ )
    {
      if (*p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim)
        {
          putc('\\', fp);
          if( *p == '\n' )
            putc('n', fp);
          else if( *p == '\r' )
            putc('r', fp);
          else if( *p == '\f' )
            putc('f', fp);
          else if( *p == '\v' )
            putc('v', fp);
          else if( *p == '\b' )
            putc('b', fp);
          else if( !*p )
            putc('0', fp);
          else
            fprintf(fp, "x%02x", *p );
	}
      else
        putc(*p, fp);
    }
}


get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey,
             unsigned char *grip)
{
  gpg_error_t err;
  gcry_sexp_t s_pkey = NULL;

  switch (pubkey_algo)
    {
    case GCRY_PK_DSA:
      err = gcry_sexp_build (&s_pkey, NULL,
                             "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
                             pkey[0], pkey[1], pkey[2], pkey[3]);
      break;

    case GCRY_PK_ELG:
      err = gcry_sexp_build (&s_pkey, NULL,
                             "(public-key(elg(p%m)(g%m)(y%m)))",
                             pkey[0], pkey[1], pkey[2]);
      break;

    case GCRY_PK_RSA:
      err = gcry_sexp_build (&s_pkey, NULL,
                             "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]);
      break;

    case GCRY_PK_ECC:
      if (!curve)
        err = gpg_error (GPG_ERR_BAD_SECKEY);
      else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
        err = gcry_sexp_build (&s_pkey, NULL,
                               "(public-key(ecc(curve %s)(flags eddsa)(q%m)))",
                               "Ed25519", pkey[0]);
      else
        err = gcry_sexp_build (&s_pkey, NULL,
                               "(public-key(ecc(curve %s)(q%m)))",
                               curve, pkey[0]);
      break;

    default:
      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
      break;
    }

  if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
    err = gpg_error (GPG_ERR_INTERNAL);

  gcry_sexp_release (s_pkey);
  return err;
}


print_p2r_unknown (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("Unknown PC_to_RDR command", msg, msglen);
  if (msglen < 10)
    return;
  print_pr_data (msg, msglen, 0);
}


iobuf_set_limit (iobuf_t a, off_t nlimit)
{
  if (nlimit)
    a->nofast |= 1;
  else
    a->nofast &= ~1;
  a->nlimit = nlimit;
  a->ntotal += a->nbytes;
  a->nbytes = 0;
}


get_ownertrust_with_min (PKT_public_key *pk)
{
#ifdef NO_TRUST_MODELS
  (void)pk;
  return TRUST_UNKNOWN;
#else
  unsigned int otrust, otrust_min;

  otrust = (tdb_get_ownertrust (pk) & TRUST_MASK);
  otrust_min = tdb_get_min_ownertrust (pk);
  if (otrust < otrust_min)
    {
      /* If the trust that the user has set is less than the trust
	 that was calculated from a trust signature chain, use the
	 higher of the two.  We do this here and not in
	 get_ownertrust since the underlying ownertrust should not
	 really be set - just the appearance of the ownertrust. */

      otrust = otrust_min;
    }

  return otrust;
#endif
}


ccid_poll (ccid_driver_t handle)
{
  int rc;
  unsigned char msg[10];
  size_t msglen;
  int i, j;

  if (handle->idev)
    {
      rc = usb_bulk_read (handle->idev,
                          handle->ep_intr,
                          (char*)msg, sizeof msg,
                          0 /* ms timeout */ );
      if (rc < 0 && errno == ETIMEDOUT)
        return 0;
    }
  else
    return 0;

  if (rc < 0)
    {
      DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno));
      return CCID_DRIVER_ERR_CARD_IO_ERROR;
    }

  msglen = rc;
  rc = 0;

  if (msglen < 1)
    {
      DEBUGOUT ("intr-in msg too short\n");
      return CCID_DRIVER_ERR_INV_VALUE;
    }

  if (msg[0] == RDR_to_PC_NotifySlotChange)
    {
      DEBUGOUT ("notify slot change:");
      for (i=1; i < msglen; i++)
        for (j=0; j < 4; j++)
          DEBUGOUT_CONT_3 (" %d:%c%c",
                           (i-1)*4+j,
                           (msg[i] & (1<<(j*2)))? 'p':'-',
                           (msg[i] & (2<<(j*2)))? '*':' ');
      DEBUGOUT_LF ();
    }
  else if (msg[0] == RDR_to_PC_HardwareError)
    {
      DEBUGOUT ("hardware error occured\n");
    }
  else
    {
      DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]);
    }

  return 0;
}


build_privkey_template (app_t app, int keyno,
                        const unsigned char *rsa_n, size_t rsa_n_len,
                        const unsigned char *rsa_e, size_t rsa_e_len,
                        const unsigned char *rsa_p, size_t rsa_p_len,
                        const unsigned char *rsa_q, size_t rsa_q_len,
                        const unsigned char *rsa_u, size_t rsa_u_len,
                        const unsigned char *rsa_dp, size_t rsa_dp_len,
                        const unsigned char *rsa_dq, size_t rsa_dq_len,
                        unsigned char **result, size_t *resultlen)
{
  size_t rsa_e_reqlen;
  unsigned char privkey[7*(1+3+3)];
  size_t privkey_len;
  unsigned char exthdr[2+2+3];
  size_t exthdr_len;
  unsigned char suffix[2+3];
  size_t suffix_len;
  unsigned char *tp;
  size_t datalen;
  unsigned char *template;
  size_t template_size;

  *result = NULL;
  *resultlen = 0;

  switch (app->app_local->keyattr[keyno].rsa.format)
    {
    case RSA_STD:
    case RSA_STD_N:
    case RSA_CRT:
    case RSA_CRT_N:
      break;

    default:
      return gpg_error (GPG_ERR_INV_VALUE);
    }

  /* Get the required length for E. Rounded up to the nearest byte  */
  rsa_e_reqlen = (app->app_local->keyattr[keyno].rsa.e_bits + 7) / 8;
  assert (rsa_e_len <= rsa_e_reqlen);

  /* Build the 7f48 cardholder private key template.  */
  datalen = 0;
  tp = privkey;

  tp += add_tlv (tp, 0x91, rsa_e_reqlen);
  datalen += rsa_e_reqlen;

  tp += add_tlv (tp, 0x92, rsa_p_len);
  datalen += rsa_p_len;

  tp += add_tlv (tp, 0x93, rsa_q_len);
  datalen += rsa_q_len;

  if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      tp += add_tlv (tp, 0x94, rsa_u_len);
      datalen += rsa_u_len;
      tp += add_tlv (tp, 0x95, rsa_dp_len);
      datalen += rsa_dp_len;
      tp += add_tlv (tp, 0x96, rsa_dq_len);
      datalen += rsa_dq_len;
    }

  if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      tp += add_tlv (tp, 0x97, rsa_n_len);
      datalen += rsa_n_len;
    }
  privkey_len = tp - privkey;

  /* Build the extended header list without the private key template.  */
  tp = exthdr;
  *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
  *tp++ = 0;
  tp += add_tlv (tp, 0x7f48, privkey_len);
  exthdr_len = tp - exthdr;

  /* Build the 5f48 suffix of the data.  */
  tp = suffix;
  tp += add_tlv (tp, 0x5f48, datalen);
  suffix_len = tp - suffix;

  /* Now concatenate everything.  */
  template_size = (1 + 3   /* 0x4d and len. */
                   + exthdr_len
                   + privkey_len
                   + suffix_len
                   + datalen);
  tp = template = xtrymalloc_secure (template_size);
  if (!template)
    return gpg_error_from_syserror ();

  tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
  memcpy (tp, exthdr, exthdr_len);
  tp += exthdr_len;
  memcpy (tp, privkey, privkey_len);
  tp += privkey_len;
  memcpy (tp, suffix, suffix_len);
  tp += suffix_len;

  memcpy (tp, rsa_e, rsa_e_len);
  if (rsa_e_len < rsa_e_reqlen)
    {
      /* Right justify E. */
      memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
      memset (tp, 0, rsa_e_reqlen - rsa_e_len);
    }
  tp += rsa_e_reqlen;

  memcpy (tp, rsa_p, rsa_p_len);
  tp += rsa_p_len;

  memcpy (tp, rsa_q, rsa_q_len);
  tp += rsa_q_len;

  if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      memcpy (tp, rsa_u, rsa_u_len);
      tp += rsa_u_len;
      memcpy (tp, rsa_dp, rsa_dp_len);
      tp += rsa_dp_len;
      memcpy (tp, rsa_dq, rsa_dq_len);
      tp += rsa_dq_len;
    }

  if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      memcpy (tp, rsa_n, rsa_n_len);
      tp += rsa_n_len;
    }

  /* Sanity check.  We don't know the exact length because we
     allocated 3 bytes for the first length header.  */
  assert (tp - template <= template_size);

  *result = template;
  *resultlen = tp - template;
  return 0;
}


dump_all_do (int slot)
{
  int rc, i, j;
  unsigned char *buffer;
  size_t buflen;

  for (i=0; data_objects[i].tag; i++)
    {
      if (data_objects[i].get_from)
        continue;

      /* We don't try extended length APDU because such large DO would
         be pretty useless in a log file.  */
      rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen);
      if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
        ;
      else if (rc)
        log_info ("DO '%s' not available: %s\n",
                  data_objects[i].desc, gpg_strerror (rc));
      else
        {
          if (data_objects[i].binary)
            {
              log_info ("DO '%s': ", data_objects[i].desc);
              log_printhex ("", buffer, buflen);
            }
          else
            log_info ("DO '%s': '%.*s'\n",
                      data_objects[i].desc,
                      (int)buflen, buffer); /* FIXME: sanitize */

          if (data_objects[i].constructed)
            {
              for (j=0; data_objects[j].tag; j++)
                {
                  const unsigned char *value;
                  size_t valuelen;

                  if (j==i || data_objects[i].tag != data_objects[j].get_from)
                    continue;
                  value = find_tlv_unchecked (buffer, buflen,
                                              data_objects[j].tag, &valuelen);
                  if (!value)
                    ; /* not found */
                  else if (valuelen > buflen - (value - buffer))
                    log_error ("warning: constructed DO too short\n");
                  else
                    {
                      if (data_objects[j].binary)
                        {
                          log_info ("DO '%s': ", data_objects[j].desc);
                          if (valuelen > 200)
                            log_info ("[%u]\n", (unsigned int)valuelen);
                          else
                            log_printhex ("", value, valuelen);
                        }
                      else
                        log_info ("DO '%s': '%.*s'\n",
                                  data_objects[j].desc,
                                  (int)valuelen, value); /* FIXME: sanitize */
                    }
                }
            }
        }
      xfree (buffer); buffer = NULL;
    }
}


read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen)
{
  gpg_error_t err;
  estream_t fp;
  unsigned char packet[32];

  *r_fp = NULL;

  fp = es_fopen (filename, "rb");
  if (!fp)
    {
      err = gpg_error_from_syserror ();
      log_error ("error reading '%s': %s\n", filename, gpg_strerror (err));
      return err;
    }

  /* Read the header.  It is defined as 32 bytes thus we read it in one go.  */
  if (es_fread (packet, 32, 1, fp) != 1)
    {
      err = gpg_error_from_syserror ();
      log_error ("error reading the header of '%s': %s\n",
                 filename, gpg_strerror (err));
      es_fclose (fp);
      return err;
    }

  err = parse_header (filename, packet, 32, r_headerlen);
  if (err)
    es_fclose (fp);
  else
    *r_fp = fp;

  return err;
}


keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
                    unsigned char *sha1_digest)
{
  int rc;
  const char *fname;
  KEYBOXBLOB blob;

  if (!hd)
    return gpg_error (GPG_ERR_INV_HANDLE);
  if (!hd->kb)
    return gpg_error (GPG_ERR_INV_HANDLE);
  fname = hd->kb->fname;
  if (!fname)
    return gpg_error (GPG_ERR_INV_HANDLE);

  /* Close this one otherwise we will mess up the position for a next
     search.  Fixme: it would be better to adjust the position after
     the write operation.  */
  _keybox_close_file (hd);

  rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
  if (!rc)
    {
      rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0, 0);
      _keybox_release_blob (blob);
      /*    if (!rc && !hd->secret && kb_offtbl) */
      /*      { */
      /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
      /*      } */
    }
  return rc;
}


get_pubkey_byfprint (PKT_public_key * pk,
		     const byte * fprint, size_t fprint_len)
{
  int rc;

  if (fprint_len == 20 || fprint_len == 16)
    {
      struct getkey_ctx_s ctx;
      KBNODE kb = NULL;

      memset (&ctx, 0, sizeof ctx);
      ctx.exact = 1;
      ctx.not_allocated = 1;
      ctx.kr_handle = keydb_new ();
      ctx.nitems = 1;
      ctx.items[0].mode = fprint_len == 16 ? KEYDB_SEARCH_MODE_FPR16
	: KEYDB_SEARCH_MODE_FPR20;
      memcpy (ctx.items[0].u.fpr, fprint, fprint_len);
      rc = lookup (&ctx, &kb, 0);
      if (!rc && pk)
	pk_from_block (&ctx, pk, kb);
      release_kbnode (kb);
      get_pubkey_end (&ctx);
    }
  else
    rc = GPG_ERR_GENERAL; /* Oops */
  return rc;
}


keybox_get_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int *value)
{
  const unsigned char *buffer;
  size_t length;
  gpg_err_code_t ec;

  (void)idx; /* Not yet used.  */

  if (!hd)
    return gpg_error (GPG_ERR_INV_VALUE);
  if (!hd->found.blob)
    return gpg_error (GPG_ERR_NOTHING_FOUND);

  buffer = _keybox_get_blob_image (hd->found.blob, &length);
  ec = get_flag_from_image (buffer, length, what, value);
  return ec? gpg_error (ec):0;
}


pcsc_error_string (long err)
{
  const char *s;

  if (!err)
    return "okay";
  if ((err & 0x80100000) != 0x80100000)
    return "invalid PC/SC error code";
  err &= 0xffff;
  switch (err)
    {
    case 0x0002: s = "cancelled"; break;
    case 0x000e: s = "can't dispose"; break;
    case 0x0008: s = "insufficient buffer"; break;
    case 0x0015: s = "invalid ATR"; break;
    case 0x0003: s = "invalid handle"; break;
    case 0x0004: s = "invalid parameter"; break;
    case 0x0005: s = "invalid target"; break;
    case 0x0011: s = "invalid value"; break;
    case 0x0006: s = "no memory"; break;
    case 0x0013: s = "comm error"; break;
    case 0x0001: s = "internal error"; break;
    case 0x0014: s = "unknown error"; break;
    case 0x0007: s = "waited too long"; break;
    case 0x0009: s = "unknown reader"; break;
    case 0x000a: s = "timeout"; break;
    case 0x000b: s = "sharing violation"; break;
    case 0x000c: s = "no smartcard"; break;
    case 0x000d: s = "unknown card"; break;
    case 0x000f: s = "proto mismatch"; break;
    case 0x0010: s = "not ready"; break;
    case 0x0012: s = "system cancelled"; break;
    case 0x0016: s = "not transacted"; break;
    case 0x0017: s = "reader unavailable"; break;
    case 0x0065: s = "unsupported card"; break;
    case 0x0066: s = "unresponsive card"; break;
    case 0x0067: s = "unpowered card"; break;
    case 0x0068: s = "reset card"; break;
    case 0x0069: s = "removed card"; break;
    case 0x006a: s = "inserted card"; break;
    case 0x001f: s = "unsupported feature"; break;
    case 0x0019: s = "PCI too small"; break;
    case 0x001a: s = "reader unsupported"; break;
    case 0x001b: s = "duplicate reader"; break;
    case 0x001c: s = "card unsupported"; break;
    case 0x001d: s = "no service"; break;
    case 0x001e: s = "service stopped"; break;
    default:     s = "unknown PC/SC error code"; break;
    }
  return s;
}


gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
              u32 *timestamp, u32 expireval)
{
#ifdef ENABLE_CARD_SUPPORT
  gpg_error_t err;
  struct agent_card_genkey_s info;
  PACKET *pkt;
  PKT_public_key *pk;

  if (algo != PUBKEY_ALGO_RSA)
    return gpg_error (GPG_ERR_PUBKEY_ALGO);

  pk = xtrycalloc (1, sizeof *pk );
  if (!pk)
    return gpg_error_from_syserror ();
  pkt = xtrycalloc (1, sizeof *pkt);
  if (!pkt)
    {
      xfree (pk);
      return gpg_error_from_syserror ();
    }

  /* Note: SCD knows the serialnumber, thus there is no point in passing it.  */
  err = agent_scd_genkey (&info, keyno, 1, NULL, *timestamp);
  /*  The code below is not used because we force creation of
   *  the a card key (3rd arg).
   * if (gpg_err_code (rc) == GPG_ERR_EEXIST)
   *   {
   *     tty_printf ("\n");
   *     log_error ("WARNING: key does already exists!\n");
   *     tty_printf ("\n");
   *     if ( cpr_get_answer_is_yes( "keygen.card.replace_key",
   *                                 _("Replace existing key? ")))
   *       rc = agent_scd_genkey (&info, keyno, 1);
   *   }
  */
  if (!err && (!info.n || !info.e))
    {
      log_error ("communication error with SCD\n");
      gcry_mpi_release (info.n);
      gcry_mpi_release (info.e);
      err =  gpg_error (GPG_ERR_GENERAL);
    }
  if (err)
    {
      log_error ("key generation failed: %s\n", gpg_strerror (err));
      xfree (pkt);
      xfree (pk);
      return err;
    }

  /* Send the learn command so that the agent creates a shadow key for
     card key.  We need to do that now so that we are able to create
     the self-signatures. */
  err = agent_scd_learn (NULL);
  if (err)
    {
      /* Oops: Card removed during generation.  */
      log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
      xfree (pkt);
      xfree (pk);
      return err;
    }

  if (*timestamp != info.created_at)
    log_info ("NOTE: the key does not use the suggested creation date\n");
  *timestamp = info.created_at;

  pk->timestamp = info.created_at;
  pk->version = 4;
  if (expireval)
    pk->expiredate = pk->timestamp + expireval;
  pk->pubkey_algo = algo;
  pk->pkey[0] = info.n;
  pk->pkey[1] = info.e;

  pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY;
  pkt->pkt.public_key = pk;
  add_kbnode (pub_root, new_kbnode (pkt));

  return 0;
#else
  (void)algo;
  (void)keyno;
  (void)is_primary;
  (void)pub_root;
  (void)timestamp;
  (void)expireval;
  return gpg_error (GPG_ERR_NOT_SUPPORTED);
#endif /*!ENABLE_CARD_SUPPORT*/
}


read_16 (IOBUF inp)
{
  unsigned short a;
  a = (unsigned short)iobuf_get_noeof (inp) << 8;
  a |= iobuf_get_noeof (inp);
  return a;
}


put_record_into_cache( ulong recno, const char *data )
{
    CACHE_CTRL r, unused;
    int dirty_count = 0;
    int clean_count = 0;

    /* see whether we already cached this one */
    for( unused = NULL, r = cache_list; r; r = r->next ) {
	if( !r->flags.used ) {
	    if( !unused )
		unused = r;
	}
	else if( r->recno == recno ) {
	    if( !r->flags.dirty ) {
		/* Hmmm: should we use a a copy and compare? */
		if( memcmp(r->data, data, TRUST_RECORD_LEN ) ) {
		    r->flags.dirty = 1;
		    cache_is_dirty = 1;
		}
	    }
	    memcpy( r->data, data, TRUST_RECORD_LEN );
	    return 0;
	}
	if( r->flags.used ) {
	    if( r->flags.dirty )
		dirty_count++;
	    else
		clean_count++;
	}
    }
    /* not in the cache: add a new entry */
    if( unused ) { /* reuse this entry */
	r = unused;
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    /* see whether we reached the limit */
    if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */
	r = xmalloc( sizeof *r );
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	r->next = cache_list;
	cache_list = r;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    /* cache is full: discard some clean entries */
    if( clean_count ) {
	int n = clean_count / 3; /* discard a third of the clean entries */
	if( !n )
	    n = 1;
	for( unused = NULL, r = cache_list; r; r = r->next ) {
	    if( r->flags.used && !r->flags.dirty ) {
		if( !unused )
		    unused = r;
		r->flags.used = 0;
		cache_entries--;
		if( !--n )
		    break;
	    }
	}
	assert( unused );
	r = unused;
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    /* no clean entries: have to flush some dirty entries */
    if( in_transaction ) {
	/* but we can't do this while in a transaction
	 * we increase the cache size instead */
	if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */
	    if( opt.debug && !(cache_entries % 100) )
		log_debug("increasing tdbio cache size\n");
	    r = xmalloc( sizeof *r );
	    r->flags.used = 1;
	    r->recno = recno;
	    memcpy( r->data, data, TRUST_RECORD_LEN );
	    r->flags.dirty = 1;
	    r->next = cache_list;
	    cache_list = r;
	    cache_is_dirty = 1;
	    cache_entries++;
	    return 0;
	}
	log_info(_("trustdb transaction too large\n"));
	return GPG_ERR_RESOURCE_LIMIT;
    }
    if( dirty_count ) {
	int n = dirty_count / 5; /* discard some dirty entries */
	if( !n )
	    n = 1;
	if( !is_locked ) {
	    if( dotlock_take( lockhandle, -1 ) )
		log_fatal("can't acquire lock - giving up\n");
	    else
		is_locked = 1;
	}
	for( unused = NULL, r = cache_list; r; r = r->next ) {
	    if( r->flags.used && r->flags.dirty ) {
		int rc = write_cache_item( r );
		if( rc )
		    return rc;
		if( !unused )
		    unused = r;
		r->flags.used = 0;
		cache_entries--;
		if( !--n )
		    break;
	    }
	}
	if( !opt.lock_once ) {
	    if( !dotlock_release( lockhandle ) )
		is_locked = 0;
	}
	assert( unused );
	r = unused;
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    BUG();
}


read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
{
  gpg_error_t err;
  size_t nread;

  while (count)
    {
      err = ksba_reader_read (reader, buffer, count, &nread);
      if (err)
        return err;
      buffer += nread;
      count -= nread;
    }
  return 0;
}


tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
{
    int i;
    ulong rnum = rec->recnum;

    fprintf(fp, "rec %5lu, ", rnum );

    switch( rec->rectype ) {
      case 0: fprintf(fp, "blank\n");
	break;
      case RECTYPE_VER: fprintf(fp,
	    "version, td=%lu, f=%lu, m/c/d=%d/%d/%d tm=%d mcl=%d nc=%lu (%s)\n",
                                   rec->r.ver.trusthashtbl,
				   rec->r.ver.firstfree,
				   rec->r.ver.marginals,
				   rec->r.ver.completes,
				   rec->r.ver.cert_depth,
				   rec->r.ver.trust_model,
				   rec->r.ver.min_cert_level,
                                   rec->r.ver.nextcheck,
				   strtimestamp(rec->r.ver.nextcheck)
                                 );
	break;
      case RECTYPE_FREE: fprintf(fp, "free, next=%lu\n", rec->r.free.next );
	break;
      case RECTYPE_HTBL:
	fprintf(fp, "htbl,");
	for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ )
	    fprintf(fp, " %lu", rec->r.htbl.item[i] );
	putc('\n', fp);
	break;
      case RECTYPE_HLST:
	fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next );
	for(i=0; i < ITEMS_PER_HLST_RECORD; i++ )
	    fprintf(fp, " %lu", rec->r.hlst.rnum[i] );
	putc('\n', fp);
	break;
      case RECTYPE_TRUST:
	fprintf(fp, "trust ");
	for(i=0; i < 20; i++ )
	    fprintf(fp, "%02X", rec->r.trust.fingerprint[i] );
        fprintf (fp, ", ot=%d, d=%d, vl=%lu\n", rec->r.trust.ownertrust,
                 rec->r.trust.depth, rec->r.trust.validlist);
	break;
      case RECTYPE_VALID:
	fprintf(fp, "valid ");
	for(i=0; i < 20; i++ )
	    fprintf(fp, "%02X", rec->r.valid.namehash[i] );
        fprintf (fp, ", v=%d, next=%lu\n", rec->r.valid.validity,
                 rec->r.valid.next);
	break;
      default:
	fprintf(fp, "unknown type %d\n", rec->rectype );
	break;
    }
}


retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
{
  gpg_error_t err = 0;
  void *relptr;
  unsigned char *value;
  size_t valuelen;

  assert (keyno >=0 && keyno <= 2);

  relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
  if (relptr && valuelen >= 60)
    bin2hex (value+keyno*20, 20, fpr);
  else
    err = gpg_error (GPG_ERR_NOT_FOUND);
  xfree (relptr);
  return err;
}


send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword,
                          int number, const unsigned char *stamp)
{
  char numbuf1[50], numbuf2[50];
  unsigned long value;

  value = buf32_to_ulong (stamp);
  if (!value)
    return;
  sprintf (numbuf1, "%d", number);
  sprintf (numbuf2, "%lu", value);
  send_status_info (ctrl, keyword,
                    numbuf1, (size_t)strlen(numbuf1),
                    numbuf2, (size_t)strlen(numbuf2), NULL, 0);
}


v3_keyid (gcry_mpi_t a, u32 *ki)
{
  byte *buffer, *p;
  size_t nbytes;

  if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &nbytes, a ))
    BUG ();
  /* fixme: allocate it on the stack */
  buffer = xmalloc (nbytes);
  if (gcry_mpi_print( GCRYMPI_FMT_USG, buffer, nbytes, NULL, a ))
    BUG ();
  if (nbytes < 8) /* oops */
    ki[0] = ki[1] = 0;
  else
    {
      p = buffer + nbytes - 8;
      ki[0] = buf32_to_u32 (p);
      p += 4;
      ki[1] = buf32_to_u32 (p);
    }
  xfree (buffer);
  return ki[1];
}


update_param_by_atr (unsigned char *param, unsigned char *atr, size_t atrlen)
{
  int i = -1;
  int t, y, chk;
  int historical_bytes_num, negotiable = 1;

#define NEXTBYTE() do { i++; if (atrlen <= i) return -1; } while (0)

  NEXTBYTE ();

  if (atr[i] == 0x3F)
    param[1] |= 0x02;           /* Convention is inverse.  */
  NEXTBYTE ();

  y = (atr[i] >> 4);
  historical_bytes_num = atr[i] & 0x0f;
  NEXTBYTE ();

  if ((y & 1))
    {
      param[0] = atr[i];        /* TA1 - Fi & Di */
      NEXTBYTE ();
    }

  if ((y & 2))
    NEXTBYTE ();                /* TB1 - ignore */

  if ((y & 4))
    {
      param[2] = atr[i];        /* TC1 - Guard Time */
      NEXTBYTE ();
    }

  if ((y & 8))
    {
      y = (atr[i] >> 4);        /* TD1 */
      t = atr[i] & 0x0f;
      NEXTBYTE ();

      if ((y & 1))
        {                       /* TA2 - PPS mode */
          if ((atr[i] & 0x0f) != 1)
            return -2;          /* Wrong card protocol (!= 1).  */

          if ((atr[i] & 0x10) != 0x10)
            return -3; /* Transmission parameters are implicitly defined. */

          negotiable = 0;       /* TA2 means specific mode.  */
          NEXTBYTE ();
        }

      if ((y & 2))
        NEXTBYTE ();            /* TB2 - ignore */

      if ((y & 4))
        NEXTBYTE ();            /* TC2 - ignore */

      if ((y & 8))
        {
          y = (atr[i] >> 4);    /* TD2 */
          t = atr[i] & 0x0f;
          NEXTBYTE ();
        }
      else
        y = 0;

      while (y)
        {
          if ((y & 1))
            {                   /* TAx */
              if (t == 1)
                param[5] = atr[i]; /* IFSC */
              else if (t == 15)
                /* XXX: check voltage? */
                param[4] = (atr[i] >> 6); /* ClockStop */

              NEXTBYTE ();
            }

          if ((y & 2))
            {
              if (t == 1)
                param[3] = atr[i]; /* TBx - BWI & CWI */
              NEXTBYTE ();
            }

          if ((y & 4))
            {
              if (t == 1)
                param[1] |= (atr[i] & 0x01); /* TCx - LRC/CRC */
              NEXTBYTE ();

              if (param[1] & 0x01)
                return -4;      /* CRC not supported yet.  */
            }

          if ((y & 8))
            {
              y = (atr[i] >> 4); /* TDx */
              t = atr[i] & 0x0f;
              NEXTBYTE ();
            }
          else
            y = 0;
        }
    }

  i += historical_bytes_num - 1;
  NEXTBYTE ();
  if (atrlen != i+1)
    return -1;

#undef NEXTBYTE

  chk = 0;
  do
    {
      chk ^= atr[i];
      i--;
    }
  while (i > 0);

  if (chk != 0)
    return -1;

  return negotiable;
}


copy_packet (IOBUF inp, IOBUF out, int pkttype,
	     unsigned long pktlen, int partial)
{
  int rc;
  int n;
  char buf[100];

  if (partial)
    {
      while ((n = iobuf_read (inp, buf, 100)) != -1)
	if ((rc = iobuf_write (out, buf, n)))
	  return rc;		/* write error */
    }
  else if (!pktlen && pkttype == PKT_COMPRESSED)
    {
      log_debug ("copy_packet: compressed!\n");
      /* compressed packet, copy till EOF */
      while ((n = iobuf_read (inp, buf, 100)) != -1)
	if ((rc = iobuf_write (out, buf, n)))
	  return rc;		/* write error */
    }
  else
    {
      for (; pktlen; pktlen -= n)
	{
	  n = pktlen > 100 ? 100 : pktlen;
	  n = iobuf_read (inp, buf, n);
	  if (n == -1)
	    return gpg_error (GPG_ERR_EOF);
	  if ((rc = iobuf_write (out, buf, n)))
	    return rc;		/* write error */
	}
    }
  return 0;
}


pubkey_get_npkey (pubkey_algo_t algo)
{
  switch (algo)
    {
    case PUBKEY_ALGO_RSA:
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_RSA_S:     return 2;
    case PUBKEY_ALGO_ELGAMAL_E: return 3;
    case PUBKEY_ALGO_DSA:       return 4;
    case PUBKEY_ALGO_ECDH:      return 3;
    case PUBKEY_ALGO_ECDSA:     return 2;
    case PUBKEY_ALGO_ELGAMAL:   return 3;
    case PUBKEY_ALGO_EDDSA:     return 2;
    }
  return 0;
}


blob_get_blob_flags (KEYBOXBLOB blob)
{
  const unsigned char *buffer;
  size_t length;

  buffer = _keybox_get_blob_image (blob, &length);
  if (length < 8)
    return 0; /* oops */

  return get16 (buffer + 6);
}


buf32_to_uint (const void *buffer)
{
  const unsigned char *p = buffer;

  return (((unsigned int)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
}


check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
{
  int r;

  if (reader_table[slot].pcsc.pinmin >= 0)
    pininfo->minlen = reader_table[slot].pcsc.pinmin;

  if (reader_table[slot].pcsc.pinmax >= 0)
    pininfo->maxlen = reader_table[slot].pcsc.pinmax;

  if (!pininfo->minlen)
    pininfo->minlen = 1;
  if (!pininfo->maxlen)
    pininfo->maxlen = 15;

  if ((command == ISO7816_VERIFY && reader_table[slot].pcsc.verify_ioctl != 0)
      || (command == ISO7816_CHANGE_REFERENCE_DATA
          && reader_table[slot].pcsc.modify_ioctl != 0))
    r = 0;                       /* Success */
  else
    r = SW_NOT_SUPPORTED;

  if (DBG_CARD_IO)
    log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
               (unsigned int)command, r);

  if (reader_table[slot].pinpad_varlen_supported)
    pininfo->fixedlen = 0;

  return r;
}


_keybox_dump_file (const char *filename, int stats_only, FILE *outfp)
{
  FILE *fp;
  KEYBOXBLOB blob;
  int rc;
  unsigned long count = 0;
  struct file_stats_s stats;

  memset (&stats, 0, sizeof stats);

  if (!(fp = open_file (&filename, outfp)))
    return gpg_error_from_syserror ();

  for (;;)
    {
      rc = _keybox_read_blob (&blob, fp);
      if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE
          && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX)
        {
          if (stats_only)
            stats.skipped_long_blobs++;
          else
            {
              fprintf (outfp, "BEGIN-RECORD: %lu\n", count );
              fprintf (outfp, "# Record too large\nEND-RECORD\n");
            }
          count++;
          continue;
        }
      if (rc)
        break;

      if (stats_only)
        {
          update_stats (blob, &stats);
        }
      else
        {
          fprintf (outfp, "BEGIN-RECORD: %lu\n", count );
          _keybox_dump_blob (blob, outfp);
          fprintf (outfp, "END-RECORD\n");
        }
      _keybox_release_blob (blob);
      count++;
    }
  if (rc == -1)
    rc = 0;
  if (rc)
    fprintf (outfp, "# error reading '%s': %s\n", filename, gpg_strerror (rc));

  if (fp != stdin)
    fclose (fp);

  if (stats_only)
    {
      fprintf (outfp,
               "Total number of blobs: %8lu\n"
               "               header: %8lu\n"
               "                empty: %8lu\n"
               "              openpgp: %8lu\n"
               "                 x509: %8lu\n"
               "          non flagged: %8lu\n"
               "       secret flagged: %8lu\n"
               "    ephemeral flagged: %8lu\n",
               stats.total_blob_count,
               stats.header_blob_count,
               stats.empty_blob_count,
               stats.pgp_blob_count,
               stats.x509_blob_count,
               stats.non_flagged,
               stats.secret_flagged,
               stats.ephemeral_flagged);
        if (stats.skipped_long_blobs)
          fprintf (outfp, "   skipped long blobs: %8lu\n",
                   stats.skipped_long_blobs);
        if (stats.unknown_blob_count)
          fprintf (outfp, "   unknown blob types: %8lu\n",
                   stats.unknown_blob_count);
        if (stats.too_short_blobs)
          fprintf (outfp, "      too short blobs: %8lu (error)\n",
                   stats.too_short_blobs);
        if (stats.too_large_blobs)
          fprintf (outfp, "      too large blobs: %8lu (error)\n",
                   stats.too_large_blobs);
    }

  return rc;
}


check_valid_days( const char *s )
{
    if( !digitp(s) )
	return 0;
    for( s++; *s; s++)
	if( !digitp(s) )
	    break;
    if( !*s )
	return 1;
    if( s[1] )
	return 0; /* e.g. "2323wc" */
    if( *s == 'd' || *s == 'D' )
	return 1;
    if( *s == 'w' || *s == 'W' )
	return 7;
    if( *s == 'm' || *s == 'M' )
	return 30;
    if( *s == 'y' || *s == 'Y' )
	return 365;
    return 0;
}


do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root,
           u32 timestamp, u32 expiredate, int is_subkey,
           int keygen_flags, const char *passphrase, char **cache_nonce_addr)
{
  gpg_error_t err;

  /* Fixme: The entropy collecting message should be moved to a
     libgcrypt progress handler.  */
  if (!opt.batch)
    tty_printf (_(
"We need to generate a lot of random bytes. It is a good idea to perform\n"
"some other action (type on the keyboard, move the mouse, utilize the\n"
"disks) during the prime generation; this gives the random number\n"
"generator a better chance to gain enough entropy.\n") );

  if (algo == PUBKEY_ALGO_ELGAMAL_E)
    err = gen_elg (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
                   keygen_flags, passphrase, cache_nonce_addr);
  else if (algo == PUBKEY_ALGO_DSA)
    err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey,
                   keygen_flags, passphrase, cache_nonce_addr);
  else if (algo == PUBKEY_ALGO_ECDSA
           || algo == PUBKEY_ALGO_EDDSA
           || algo == PUBKEY_ALGO_ECDH)
    err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey,
                   keygen_flags, passphrase, cache_nonce_addr);
  else if (algo == PUBKEY_ALGO_RSA)
    err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
                   keygen_flags, passphrase, cache_nonce_addr);
  else
    BUG();

  return err;
}


agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
              const char *keyparms, int no_protection,
              const char *passphrase, gcry_sexp_t *r_pubkey)
{
  gpg_error_t err;
  struct genkey_parm_s gk_parm;
  struct cache_nonce_parm_s cn_parm;
  struct default_inq_parm_s dfltparm;
  membuf_t data;
  size_t len;
  unsigned char *buf;
  char line[ASSUAN_LINELENGTH];

  memset (&dfltparm, 0, sizeof dfltparm);
  dfltparm.ctrl = ctrl;

  *r_pubkey = NULL;
  err = start_agent (ctrl, 0);
  if (err)
    return err;
  dfltparm.ctx = agent_ctx;

  err = assuan_transact (agent_ctx, "RESET",
                         NULL, NULL, NULL, NULL, NULL, NULL);
  if (err)
    return err;

  init_membuf (&data, 1024);
  gk_parm.dflt     = &dfltparm;
  gk_parm.keyparms = keyparms;
  gk_parm.passphrase = passphrase;
  snprintf (line, sizeof line, "GENKEY%s%s%s",
            no_protection? " --no-protection" :
            passphrase   ? " --inq-passwd" :
            /*          */ "",
            cache_nonce_addr && *cache_nonce_addr? " ":"",
            cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"");
  cn_parm.cache_nonce_addr = cache_nonce_addr;
  cn_parm.passwd_nonce_addr = NULL;
  err = assuan_transact (agent_ctx, line,
                         membuf_data_cb, &data,
                         inq_genkey_parms, &gk_parm,
                         cache_nonce_status_cb, &cn_parm);
  if (err)
    {
      xfree (get_membuf (&data, &len));
      return err;
    }

  buf = get_membuf (&data, &len);
  if (!buf)
    err = gpg_error_from_syserror ();
  else
    {
      err = gcry_sexp_sscan (r_pubkey, NULL, buf, len);
      xfree (buf);
    }
  return err;
}


keybox_delete (KEYBOX_HANDLE hd)
{
  off_t off;
  const char *fname;
  FILE *fp;
  int rc;

  if (!hd)
    return gpg_error (GPG_ERR_INV_VALUE);
  if (!hd->found.blob)
    return gpg_error (GPG_ERR_NOTHING_FOUND);
  if (!hd->kb)
    return gpg_error (GPG_ERR_INV_HANDLE);
  fname = hd->kb->fname;
  if (!fname)
    return gpg_error (GPG_ERR_INV_HANDLE);

  off = _keybox_get_blob_fileoffset (hd->found.blob);
  if (off == (off_t)-1)
    return gpg_error (GPG_ERR_GENERAL);
  off += 4;

  _keybox_close_file (hd);
  fp = fopen (hd->kb->fname, "r+b");
  if (!fp)
    return gpg_error_from_syserror ();

  if (fseeko (fp, off, SEEK_SET))
    rc = gpg_error_from_syserror ();
  else if (putc (0, fp) == EOF)
    rc = gpg_error_from_syserror ();
  else
    rc = 0;

  if (fclose (fp))
    {
      if (!rc)
        rc = gpg_error_from_syserror ();
    }

  return rc;
}


can_handle_critical (const byte * buffer, size_t n, int type)
{
  switch (type)
    {
    case SIGSUBPKT_NOTATION:
      if (n >= 8)
	{
	  size_t notation_len = ((buffer[4] << 8) | buffer[5]);
	  if (n - 8 >= notation_len)
	    return can_handle_critical_notation (buffer + 8, notation_len);
	}
      return 0;
    case SIGSUBPKT_SIGNATURE:
    case SIGSUBPKT_SIG_CREATED:
    case SIGSUBPKT_SIG_EXPIRE:
    case SIGSUBPKT_KEY_EXPIRE:
    case SIGSUBPKT_EXPORTABLE:
    case SIGSUBPKT_REVOCABLE:
    case SIGSUBPKT_REV_KEY:
    case SIGSUBPKT_ISSUER:	/* issuer key ID */
    case SIGSUBPKT_PREF_SYM:
    case SIGSUBPKT_PREF_HASH:
    case SIGSUBPKT_PREF_COMPR:
    case SIGSUBPKT_KEY_FLAGS:
    case SIGSUBPKT_PRIMARY_UID:
    case SIGSUBPKT_FEATURES:
    case SIGSUBPKT_TRUST:
    case SIGSUBPKT_REGEXP:
      /* Is it enough to show the policy or keyserver? */
    case SIGSUBPKT_POLICY:
    case SIGSUBPKT_PREF_KS:
      return 1;

    default:
      return 0;
    }
}


open_pcsc_reader_wrapped (const char *portstr)
{
  int slot;
  reader_table_t slotp;
  int fd, rp[2], wp[2];
  int n, i;
  pid_t pid;
  size_t len;
  unsigned char msgbuf[9];
  int err;
  unsigned int dummy_status;

  /* Note that we use the constant and not the fucntion because this
     code won't be be used under Windows.  */
  const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper";

  if (access (wrapperpgm, X_OK))
    {
      log_error ("can't run PC/SC access module '%s': %s\n",
                 wrapperpgm, strerror (errno));
      return -1;
    }

  slot = new_reader_slot ();
  if (slot == -1)
    return -1;
  slotp = reader_table + slot;

  /* Fire up the PC/SCc wrapper.  We don't use any fork/exec code from
     the common directy but implement it directly so that this file
     may still be source copied. */

  if (pipe (rp) == -1)
    {
      log_error ("error creating a pipe: %s\n", strerror (errno));
      slotp->used = 0;
      unlock_slot (slot);
      return -1;
    }
  if (pipe (wp) == -1)
    {
      log_error ("error creating a pipe: %s\n", strerror (errno));
      close (rp[0]);
      close (rp[1]);
      slotp->used = 0;
      unlock_slot (slot);
      return -1;
    }

  pid = fork ();
  if (pid == -1)
    {
      log_error ("error forking process: %s\n", strerror (errno));
      close (rp[0]);
      close (rp[1]);
      close (wp[0]);
      close (wp[1]);
      slotp->used = 0;
      unlock_slot (slot);
      return -1;
    }
  slotp->pcsc.pid = pid;

  if (!pid)
    { /*
         === Child ===
       */

      /* Double fork. */
      pid = fork ();
      if (pid == -1)
        _exit (31);
      if (pid)
        _exit (0); /* Immediate exit this parent, so that the child
                      gets cleaned up by the init process. */

      /* Connect our pipes. */
      if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
      if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
        log_fatal ("dup2 stdout failed: %s\n", strerror (errno));

      /* Send stderr to the bit bucket. */
      fd = open ("/dev/null", O_WRONLY);
      if (fd == -1)
        log_fatal ("can't open '/dev/null': %s", strerror (errno));
      if (fd != 2 && dup2 (fd, 2) == -1)
        log_fatal ("dup2 stderr failed: %s\n", strerror (errno));

      /* Close all other files. */
      close_all_fds (3, NULL);

      execl (wrapperpgm,
             "pcsc-wrapper",
             "--",
             "1", /* API version */
             opt.pcsc_driver, /* Name of the PC/SC library. */
              NULL);
      _exit (31);
    }

  /*
     === Parent ===
   */
  close (wp[0]);
  close (rp[1]);
  slotp->pcsc.req_fd = wp[1];
  slotp->pcsc.rsp_fd = rp[0];

  /* Wait for the intermediate child to terminate. */
#ifdef USE_NPTH
#define WAIT npth_waitpid
#else
#define WAIT waitpid
#endif
  while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
    ;
#undef WAIT

  /* Now send the open request. */
  msgbuf[0] = 0x01; /* OPEN command. */
  len = portstr? strlen (portstr):0;
  msgbuf[1] = (len >> 24);
  msgbuf[2] = (len >> 16);
  msgbuf[3] = (len >>  8);
  msgbuf[4] = (len      );
  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
       || (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
    {
      log_error ("error sending PC/SC OPEN request: %s\n",
                 strerror (errno));
      goto command_failed;
    }
  /* Read the response. */
  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
    {
      log_error ("error receiving PC/SC OPEN response: %s\n",
                 i? strerror (errno) : "premature EOF");
      goto command_failed;
    }
  len = buf32_to_size_t (msgbuf+1);
  if (msgbuf[0] != 0x81 || len < 4)
    {
      log_error ("invalid response header from PC/SC received\n");
      goto command_failed;
    }
  len -= 4; /* Already read the error code. */
  if (len > DIM (slotp->atr))
    {
      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
                 (unsigned long)len);
      goto command_failed;
    }
  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
  if (err)
    {
      log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
      goto command_failed;
    }

  slotp->last_status = 0;

  /* The open request may return a zero for the ATR length to
     indicate that no card is present.  */
  n = len;
  if (n)
    {
      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
        {
          log_error ("error receiving PC/SC OPEN response: %s\n",
                     i? strerror (errno) : "premature EOF");
          goto command_failed;
        }
      /* If we got to here we know that a card is present
         and usable.  Thus remember this.  */
      slotp->last_status = (  APDU_CARD_USABLE
                            | APDU_CARD_PRESENT
                            | APDU_CARD_ACTIVE);
    }
  slotp->atrlen = len;

  reader_table[slot].close_reader = close_pcsc_reader;
  reader_table[slot].reset_reader = reset_pcsc_reader;
  reader_table[slot].get_status_reader = pcsc_get_status;
  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;

  pcsc_vendor_specific_init (slot);

  /* Read the status so that IS_T0 will be set. */
  pcsc_get_status (slot, &dummy_status);

  dump_reader_status (slot);
  unlock_slot (slot);
  return slot;

 command_failed:
  close (slotp->pcsc.req_fd);
  close (slotp->pcsc.rsp_fd);
  slotp->pcsc.req_fd = -1;
  slotp->pcsc.rsp_fd = -1;
  if (slotp->pcsc.pid != -1)
    kill (slotp->pcsc.pid, SIGTERM);
  slotp->pcsc.pid = (pid_t)(-1);
  slotp->used = 0;
  unlock_slot (slot);
  /* There is no way to return SW. */
  return -1;

}


dump_header_blob (const byte *buffer, size_t length, FILE *fp)
{
  unsigned long n;

  if (length < 32)
    {
      fprintf (fp, "[blob too short]\n");
      return -1;
    }
  fprintf (fp, "Version: %d\n", buffer[5]);

  n = get16 (buffer + 6);
  fprintf( fp, "Flags:   %04lX", n);
  if (n)
    {
      int any = 0;

      fputs (" (", fp);
      if ((n & 2))
        {
          if (any)
            putc (',', fp);
          fputs ("openpgp", fp);
          any++;
        }
      putc (')', fp);
    }
  putc ('\n', fp);

  if ( memcmp (buffer+8, "KBXf", 4))
    fprintf (fp, "[Error: invalid magic number]\n");

  n = get32 (buffer+16);
  fprintf( fp, "created-at: %lu\n", n );
  n = get32 (buffer+20);
  fprintf( fp, "last-maint: %lu\n", n );

  return 0;
}


parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
	   byte * hdr, int hdrlen, PACKET * pkt)
{
  gpg_error_t err = 0;
  int i, version, algorithm;
  unsigned long timestamp, expiredate, max_expiredate;
  int npkey, nskey;
  u32 keyid[2];
  PKT_public_key *pk;

  (void) hdr;

  pk = pkt->pkt.public_key; /* PK has been cleared. */

  version = iobuf_get_noeof (inp);
  pktlen--;
  if (pkttype == PKT_PUBLIC_SUBKEY && version == '#')
    {
      /* Early versions of G10 used the old PGP comments packets;
       * luckily all those comments are started by a hash.  */
      if (list_mode)
	{
	  es_fprintf (listfp, ":rfc1991 comment packet: \"");
	  for (; pktlen; pktlen--)
	    {
	      int c;
	      c = iobuf_get (inp);
              if (c == -1)
                break; /* Ooops: shorter than indicated.  */
	      if (c >= ' ' && c <= 'z')
		es_putc (c, listfp);
	      else
		es_fprintf (listfp, "\\x%02x", c);
	    }
	  es_fprintf (listfp, "\"\n");
	}
      iobuf_skip_rest (inp, pktlen, 0);
      return 0;
    }
  else if (version == 4)
    {
      /* The only supported version.  Use an older gpg
         version (i.e. gpg 1.4) to parse v3 packets.  */
    }
  else if (version == 2 || version == 3)
    {
      if (opt.verbose > 1)
        log_info ("packet(%d) with obsolete version %d\n", pkttype, version);
      if (list_mode)
        es_fprintf (listfp, ":key packet: [obsolete version %d]\n", version);
      pk->version = version;
      err = gpg_error (GPG_ERR_LEGACY_KEY);
      goto leave;
    }
  else
    {
      log_error ("packet(%d) with unknown version %d\n", pkttype, version);
      if (list_mode)
        es_fputs (":key packet: [unknown version]\n", listfp);
      err = gpg_error (GPG_ERR_INV_PACKET);
      goto leave;
    }

  if (pktlen < 11)
    {
      log_error ("packet(%d) too short\n", pkttype);
      if (list_mode)
        es_fputs (":key packet: [too short]\n", listfp);
      err = gpg_error (GPG_ERR_INV_PACKET);
      goto leave;
    }
  else if (pktlen > MAX_KEY_PACKET_LENGTH)
    {
      log_error ("packet(%d) too large\n", pkttype);
      if (list_mode)
        es_fputs (":key packet: [too larget]\n", listfp);
      err = gpg_error (GPG_ERR_INV_PACKET);
      goto leave;
    }

  timestamp = read_32 (inp);
  pktlen -= 4;
  expiredate = 0;		/* have to get it from the selfsignature */
  max_expiredate = 0;
  algorithm = iobuf_get_noeof (inp);
  pktlen--;
  if (list_mode)
    es_fprintf (listfp, ":%s key packet:\n"
                "\tversion %d, algo %d, created %lu, expires %lu\n",
                pkttype == PKT_PUBLIC_KEY ? "public" :
                pkttype == PKT_SECRET_KEY ? "secret" :
                pkttype == PKT_PUBLIC_SUBKEY ? "public sub" :
                pkttype == PKT_SECRET_SUBKEY ? "secret sub" : "??",
                version, algorithm, timestamp, expiredate);

  pk->timestamp = timestamp;
  pk->expiredate = expiredate;
  pk->max_expiredate = max_expiredate;
  pk->hdrbytes = hdrlen;
  pk->version = version;
  pk->flags.primary = (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY);
  pk->pubkey_algo = algorithm;

  nskey = pubkey_get_nskey (algorithm);
  npkey = pubkey_get_npkey (algorithm);
  if (!npkey)
    {
      if (list_mode)
	es_fprintf (listfp, "\tunknown algorithm %d\n", algorithm);
      unknown_pubkey_warning (algorithm);
    }

  if (!npkey)
    {
      /* Unknown algorithm - put data into an opaque MPI.  */
      pk->pkey[0] = gcry_mpi_set_opaque (NULL,
                                         read_rest (inp, pktlen), pktlen * 8);
      pktlen = 0;
      goto leave;
    }
  else
    {
      for (i = 0; i < npkey; i++)
        {
          if (    (algorithm == PUBKEY_ALGO_ECDSA && (i == 0))
               || (algorithm == PUBKEY_ALGO_EDDSA && (i == 0))
               || (algorithm == PUBKEY_ALGO_ECDH  && (i == 0 || i == 2)))
            {
              /* Read the OID (i==1) or the KDF params (i==2).  */
              size_t n;
	      err = read_size_body (inp, pktlen, &n, pk->pkey+i);
              pktlen -= n;
            }
          else
            {
              unsigned int n = pktlen;
              pk->pkey[i] = mpi_read (inp, &n, 0);
              pktlen -= n;
              if (!pk->pkey[i])
                err = gpg_error (GPG_ERR_INV_PACKET);
            }
          if (err)
            goto leave;
          if (list_mode)
            {
              es_fprintf (listfp, "\tpkey[%d]: ", i);
              mpi_print (listfp, pk->pkey[i], mpi_print_mode);
              if ((algorithm == PUBKEY_ALGO_ECDSA
                   || algorithm == PUBKEY_ALGO_EDDSA
                   || algorithm == PUBKEY_ALGO_ECDH) && i==0)
                {
                  char *curve = openpgp_oid_to_str (pk->pkey[0]);
                  es_fprintf (listfp, " %s (%s)",
                              openpgp_oid_to_curve (curve), curve);
                  xfree (curve);
                }
              es_putc ('\n', listfp);
            }
        }
    }
  if (list_mode)
    keyid_from_pk (pk, keyid);

  if (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY)
    {
      struct seckey_info *ski;
      byte temp[16];
      size_t snlen = 0;

      pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
      if (!pk->seckey_info)
        {
          err = gpg_error_from_syserror ();
          goto leave;
        }

      ski->algo = iobuf_get_noeof (inp);
      pktlen--;
      if (ski->algo)
	{
	  ski->is_protected = 1;
	  ski->s2k.count = 0;
	  if (ski->algo == 254 || ski->algo == 255)
	    {
	      if (pktlen < 3)
		{
		  err = gpg_error (GPG_ERR_INV_PACKET);
		  goto leave;
		}
	      ski->sha1chk = (ski->algo == 254);
	      ski->algo = iobuf_get_noeof (inp);
	      pktlen--;
	      /* Note that a ski->algo > 110 is illegal, but I'm not
	         erroring on it here as otherwise there would be no
	         way to delete such a key.  */
	      ski->s2k.mode = iobuf_get_noeof (inp);
	      pktlen--;
	      ski->s2k.hash_algo = iobuf_get_noeof (inp);
	      pktlen--;
	      /* Check for the special GNU extension.  */
	      if (ski->s2k.mode == 101)
		{
		  for (i = 0; i < 4 && pktlen; i++, pktlen--)
		    temp[i] = iobuf_get_noeof (inp);
		  if (i < 4 || memcmp (temp, "GNU", 3))
		    {
		      if (list_mode)
			es_fprintf (listfp, "\tunknown S2K %d\n",
                                    ski->s2k.mode);
		      err = gpg_error (GPG_ERR_INV_PACKET);
		      goto leave;
		    }
		  /* Here we know that it is a GNU extension.  What
		   * follows is the GNU protection mode: All values
		   * have special meanings and they are mapped to MODE
		   * with a base of 1000.  */
		  ski->s2k.mode = 1000 + temp[3];
		}

              /* Read the salt.  */
	      switch (ski->s2k.mode)
		{
		case 1:
		case 3:
		  for (i = 0; i < 8 && pktlen; i++, pktlen--)
		    temp[i] = iobuf_get_noeof (inp);
		  memcpy (ski->s2k.salt, temp, 8);
		  break;
		}

              /* Check the mode.  */
	      switch (ski->s2k.mode)
		{
		case 0:
		  if (list_mode)
		    es_fprintf (listfp, "\tsimple S2K");
		  break;
		case 1:
		  if (list_mode)
		    es_fprintf (listfp, "\tsalted S2K");
		  break;
		case 3:
		  if (list_mode)
		    es_fprintf (listfp, "\titer+salt S2K");
		  break;
		case 1001:
		  if (list_mode)
		    es_fprintf (listfp, "\tgnu-dummy S2K");
		  break;
		case 1002:
		  if (list_mode)
		    es_fprintf (listfp, "\tgnu-divert-to-card S2K");
		  break;
		default:
		  if (list_mode)
		    es_fprintf (listfp, "\tunknown %sS2K %d\n",
                                ski->s2k.mode < 1000 ? "" : "GNU ",
                                ski->s2k.mode);
		  err = gpg_error (GPG_ERR_INV_PACKET);
		  goto leave;
		}

              /* Print some info.  */
	      if (list_mode)
		{
		  es_fprintf (listfp, ", algo: %d,%s hash: %d",
                              ski->algo,
                              ski->sha1chk ? " SHA1 protection,"
                              : " simple checksum,", ski->s2k.hash_algo);
		  if (ski->s2k.mode == 1 || ski->s2k.mode == 3)
		    {
		      es_fprintf (listfp, ", salt: ");
                      es_write_hexstring (listfp, ski->s2k.salt, 8, 0, NULL);
		    }
		  es_putc ('\n', listfp);
		}

              /* Read remaining protection parameters.  */
	      if (ski->s2k.mode == 3)
		{
		  if (pktlen < 1)
		    {
		      err = gpg_error (GPG_ERR_INV_PACKET);
		      goto leave;
		    }
		  ski->s2k.count = iobuf_get (inp);
		  pktlen--;
		  if (list_mode)
		    es_fprintf (listfp, "\tprotect count: %lu (%lu)\n",
                                (ulong)S2K_DECODE_COUNT ((ulong)ski->s2k.count),
                                (ulong) ski->s2k.count);
		}
	      else if (ski->s2k.mode == 1002)
		{
		  /* Read the serial number. */
		  if (pktlen < 1)
		    {
		      err = gpg_error (GPG_ERR_INV_PACKET);
		      goto leave;
		    }
		  snlen = iobuf_get (inp);
		  pktlen--;
		  if (pktlen < snlen || snlen == (size_t)(-1))
		    {
		      err = gpg_error (GPG_ERR_INV_PACKET);
		      goto leave;
		    }
		}
	    }
	  else /* Old version; no S2K, so we set mode to 0, hash MD5.  */
	    {
              /* Note that a ski->algo > 110 is illegal, but I'm not
                 erroring on it here as otherwise there would be no
                 way to delete such a key.  */
	      ski->s2k.mode = 0;
	      ski->s2k.hash_algo = DIGEST_ALGO_MD5;
	      if (list_mode)
		es_fprintf (listfp, "\tprotect algo: %d  (hash algo: %d)\n",
                            ski->algo, ski->s2k.hash_algo);
	    }

	  /* It is really ugly that we don't know the size
	   * of the IV here in cases we are not aware of the algorithm.
	   * so a
	   *   ski->ivlen = cipher_get_blocksize (ski->algo);
	   * won't work.  The only solution I see is to hardwire it.
	   * NOTE: if you change the ivlen above 16, don't forget to
	   * enlarge temp.  */
	  ski->ivlen = openpgp_cipher_blocklen (ski->algo);
	  assert (ski->ivlen <= sizeof (temp));

	  if (ski->s2k.mode == 1001)
	    ski->ivlen = 0;
	  else if (ski->s2k.mode == 1002)
	    ski->ivlen = snlen < 16 ? snlen : 16;

	  if (pktlen < ski->ivlen)
	    {
              err = gpg_error (GPG_ERR_INV_PACKET);
	      goto leave;
	    }
	  for (i = 0; i < ski->ivlen && pktlen; i++, pktlen--)
	    temp[i] = iobuf_get_noeof (inp);
	  if (list_mode)
	    {
	      es_fprintf (listfp,
                          ski->s2k.mode == 1002 ? "\tserial-number: "
                          : "\tprotect IV: ");
	      for (i = 0; i < ski->ivlen; i++)
		es_fprintf (listfp, " %02x", temp[i]);
	      es_putc ('\n', listfp);
	    }
	  memcpy (ski->iv, temp, ski->ivlen);
	}

      /* It does not make sense to read it into secure memory.
       * If the user is so careless, not to protect his secret key,
       * we can assume, that he operates an open system :=(.
       * So we put the key into secure memory when we unprotect it. */
      if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002)
	{
	  /* Better set some dummy stuff here.  */
	  pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
						 xstrdup ("dummydata"),
						 10 * 8);
	  pktlen = 0;
	}
      else if (ski->is_protected)
	{
	  /* Ugly: The length is encrypted too, so we read all stuff
	   * up to the end of the packet into the first SKEY
	   * element.  */
	  pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
						 read_rest (inp, pktlen),
						 pktlen * 8);
          /* Mark that MPI as protected - we need this information for
             importing a key.  The OPAQUE flag can't be used because
             we also store public EdDSA values in opaque MPIs.  */
          if (pk->pkey[npkey])
            gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1);
	  pktlen = 0;
	  if (list_mode)
            es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey);
	}
      else
	{
          /* Not encrypted.  */
	  for (i = npkey; i < nskey; i++)
	    {
              unsigned int n = pktlen;
              pk->pkey[i] = mpi_read (inp, &n, 0);
              pktlen -= n;
              if (list_mode)
                {
                  es_fprintf (listfp, "\tskey[%d]: ", i);
                  mpi_print (listfp, pk->pkey[i], mpi_print_mode);
                  es_putc ('\n', listfp);
                }

	      if (!pk->pkey[i])
		err = gpg_error (GPG_ERR_INV_PACKET);
	    }
	  if (err)
	    goto leave;

	  ski->csum = read_16 (inp);
	  pktlen -= 2;
	  if (list_mode)
            es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum);
	}
    }

  if (list_mode)
    es_fprintf (listfp, "\tkeyid: %08lX%08lX\n",
                (ulong) keyid[0], (ulong) keyid[1]);

 leave:
  iobuf_skip_rest (inp, pktlen, 0);
  return err;
}


dump_keyblob (tupledesc_t tuples)
{
  size_t n;
  unsigned int tag;
  const void *value;

  log_info ("keyblob dump:\n");
  tag = KEYBLOB_TAG_BLOBVERSION;
  value = find_tuple (tuples, tag, &n);
  while (value)
    {
      log_info ("   tag: %-5u len: %-2u value: ", tag, (unsigned int)n);
      if (tag == KEYBLOB_TAG_ENCKEY
          ||  tag == KEYBLOB_TAG_MACKEY)
        log_printf ("[confidential]\n");
      else if (!n)
        log_printf ("[none]\n");
      else
        log_printhex ("", value, n);
      value = next_tuple (tuples, &tag, &n);
    }
}


agent_scd_writekey (int keyno, const char *serialno,
                    const unsigned char *keydata, size_t keydatalen)
{
  int rc;
  char line[ASSUAN_LINELENGTH];
  struct writekey_parm_s parms;
  struct default_inq_parm_s dfltparm;

  memset (&dfltparm, 0, sizeof dfltparm);

  (void)serialno;

  rc = start_agent (NULL, 1);
  if (rc)
    return rc;

  memset (&parms, 0, sizeof parms);

  snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno);
  line[DIM(line)-1] = 0;
  dfltparm.ctx = agent_ctx;
  parms.dflt = &dfltparm;
  parms.keydata = keydata;
  parms.keydatalen = keydatalen;

  rc = assuan_transact (agent_ctx, line, NULL, NULL,
                        inq_writekey_parms, &parms, NULL, NULL);

  status_sc_op_failure (rc);
  return rc;
}


print_pubkey_algo_note (pubkey_algo_t algo)
{
  if(algo >= 100 && algo <= 110)
    {
      static int warn=0;
      if(!warn)
	{
	  warn=1;
          es_fflush (es_stdout);
	  log_info (_("WARNING: using experimental public key algorithm %s\n"),
		    openpgp_pk_algo_name (algo));
	}
    }
  else if (algo == PUBKEY_ALGO_ELGAMAL)
    {
      es_fflush (es_stdout);
      log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n"));
    }
}


print_p2r_escape (const unsigned char *msg, size_t msglen)
{
  if (skip_escape)
    return;
  print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
  print_pr_data (msg, msglen, 7);
}


string_to_notation(const char *string,int is_utf8)
{
  const char *s;
  int saw_at=0;
  struct notation *notation;

  notation=xmalloc_clear(sizeof(*notation));

  if(*string=='-')
    {
      notation->flags.ignore=1;
      string++;
    }

  if(*string=='!')
    {
      notation->flags.critical=1;
      string++;
    }

  /* If and when the IETF assigns some official name tags, we'll have
     to add them here. */

  for( s=string ; *s != '='; s++ )
    {
      if( *s=='@')
	saw_at++;

      /* -notationname is legal without an = sign */
      if(!*s && notation->flags.ignore)
	break;

      if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) )
	{
	  log_error(_("a notation name must have only printable characters"
		      " or spaces, and end with an '='\n") );
	  goto fail;
	}
    }

  notation->name=xmalloc((s-string)+1);
  strncpy(notation->name,string,s-string);
  notation->name[s-string]='\0';

  if(!saw_at && !opt.expert)
    {
      log_error(_("a user notation name must contain the '@' character\n"));
      goto fail;
    }

  if (saw_at > 1)
    {
      log_error(_("a notation name must not contain more than"
		  " one '@' character\n"));
      goto fail;
    }

  if(*s)
    {
      const char *i=s+1;
      int highbit=0;

      /* we only support printable text - therefore we enforce the use
	 of only printable characters (an empty value is valid) */
      for(s++; *s ; s++ )
	{
	  if ( !isascii (*s) )
	    highbit=1;
	  else if (iscntrl(*s))
	    {
	      log_error(_("a notation value must not use any"
			  " control characters\n"));
	      goto fail;
	    }
	}

      if(!highbit || is_utf8)
	notation->value=xstrdup(i);
      else
	notation->value=native_to_utf8(i);
    }

  return notation;

 fail:
  free_notation(notation);
  return NULL;
}


keygen_add_std_prefs (PKT_signature *sig, void *opaque)
{
  PKT_public_key *pk = opaque;

  do_add_key_flags (sig, pk->pubkey_usage);
  keygen_add_key_expire (sig, opaque );
  keygen_upd_std_prefs (sig, opaque);
  keygen_add_keyserver_url (sig,NULL);

  return 0;
}


parse_parameter_usage (const char *fname,
                       struct para_data_s *para, enum para_name key)
{
    struct para_data_s *r = get_parameter( para, key );
    char *p, *pn;
    unsigned int use;

    if( !r )
	return 0; /* none (this is an optional parameter)*/

    use = 0;
    pn = r->u.value;
    while ( (p = strsep (&pn, " \t,")) ) {
        if ( !*p)
            ;
        else if ( !ascii_strcasecmp (p, "sign") )
            use |= PUBKEY_USAGE_SIG;
        else if ( !ascii_strcasecmp (p, "encrypt") )
            use |= PUBKEY_USAGE_ENC;
        else if ( !ascii_strcasecmp (p, "auth") )
            use |= PUBKEY_USAGE_AUTH;
        else {
            log_error("%s:%d: invalid usage list\n", fname, r->lnr );
            return -1; /* error */
        }
    }
    r->u.usage = use;
    return 1;
}


iobuf_get_fd (iobuf_t a)
{
  if (a->directfp)
    return fileno ( (FILE*)a->directfp );

  for ( ; a; a = a->chain )
    if (!a->chain && a->filter == file_filter)
      {
        file_filter_ctx_t *b = a->filter_ov;
        gnupg_fd_t fp = b->fp;

        return FD2INT (fp);
      }

  return -1;
}


has_issuer (KEYBOXBLOB blob, const char *name)
{
  size_t namelen;

  return_val_if_fail (name, 0);

  if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509)
    return 0;

  namelen = strlen (name);
  return blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0, 1);
}


die (const char *format, ...)
{
  va_list arg_ptr;

  fflush (stdout);
  fprintf (stderr, "%s: ", PGM);

  va_start (arg_ptr, format);
  vfprintf (stderr, format, arg_ptr);
  va_end (arg_ptr);
  putc ('\n', stderr);

  exit (1);
}


agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
                  const void *key, size_t keylen, int unattended)
{
  gpg_error_t err;
  struct import_key_parm_s parm;
  struct cache_nonce_parm_s cn_parm;
  char line[ASSUAN_LINELENGTH];
  struct default_inq_parm_s dfltparm;

  memset (&dfltparm, 0, sizeof dfltparm);
  dfltparm.ctrl = ctrl;

  err = start_agent (ctrl, 0);
  if (err)
    return err;
  dfltparm.ctx = agent_ctx;

  if (desc)
    {
      snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
      line[DIM(line)-1] = 0;
      err = assuan_transact (agent_ctx, line,
                            NULL, NULL, NULL, NULL, NULL, NULL);
      if (err)
        return err;
    }

  parm.dflt   = &dfltparm;
  parm.key    = key;
  parm.keylen = keylen;

  snprintf (line, sizeof line, "IMPORT_KEY%s%s%s",
            unattended? " --unattended":"",
            cache_nonce_addr && *cache_nonce_addr? " ":"",
            cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"");
  cn_parm.cache_nonce_addr = cache_nonce_addr;
  cn_parm.passwd_nonce_addr = NULL;
  err = assuan_transact (agent_ctx, line,
                         NULL, NULL,
                         inq_import_key_parms, &parm,
                         cache_nonce_status_cb, &cn_parm);
  return err;
}


gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a)
{
  int rc;

  if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
    {
      unsigned int nbits;
      const void *p;

      p = gcry_mpi_get_opaque (a, &nbits);
      rc = iobuf_write (out, p, (nbits+7)/8);
    }
  else
    rc = gpg_error (GPG_ERR_BAD_MPI);

  return rc;
}


buf16_to_ulong (const void *buffer)
{
  const unsigned char *p = buffer;

  return (((unsigned long)p[0] << 8) | p[1]);
}


keygen_add_keyserver_url(PKT_signature *sig, void *opaque)
{
  const char *url=opaque;

  if(!url)
    url=opt.def_keyserver_url;

  if(url)
    build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url));
  else
    delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS);

  return 0;
}


blob_cmp_name (KEYBOXBLOB blob, int idx,
               const char *name, size_t namelen, int substr, int x509)
{
  const unsigned char *buffer;
  size_t length;
  size_t pos, off, len;
  size_t nkeys, keyinfolen;
  size_t nuids, uidinfolen;
  size_t nserial;

  buffer = _keybox_get_blob_image (blob, &length);
  if (length < 40)
    return 0; /* blob too short */

  /*keys*/
  nkeys = get16 (buffer + 16);
  keyinfolen = get16 (buffer + 18 );
  if (keyinfolen < 28)
    return 0; /* invalid blob */
  pos = 20 + keyinfolen*nkeys;
  if (pos+2 > length)
    return 0; /* out of bounds */

  /*serial*/
  nserial = get16 (buffer+pos);
  pos += 2 + nserial;
  if (pos+4 > length)
    return 0; /* out of bounds */

  /* user ids*/
  nuids = get16 (buffer + pos);  pos += 2;
  uidinfolen = get16 (buffer + pos);  pos += 2;
  if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */)
    return 0; /* invalid blob */
  if (pos + uidinfolen*nuids > length)
    return 0; /* out of bounds */

  if (idx < 0)
    { /* Compare all names.  Note that for X.509 we start with index 1
         so to skip the issuer at index 0.  */
      for (idx = !!x509; idx < nuids; idx++)
        {
          size_t mypos = pos;

          mypos += idx*uidinfolen;
          off = get32 (buffer+mypos);
          len = get32 (buffer+mypos+4);
          if (off+len > length)
            return 0; /* error: better stop here out of bounds */
          if (len < 1)
            continue; /* empty name */
          if (substr)
            {
              if (ascii_memcasemem (buffer+off, len, name, namelen))
                return idx+1; /* found */
            }
          else
            {
              if (len == namelen && !memcmp (buffer+off, name, len))
                return idx+1; /* found */
            }
        }
    }
  else
    {
      if (idx > nuids)
        return 0; /* no user ID with that idx */
      pos += idx*uidinfolen;
      off = get32 (buffer+pos);
      len = get32 (buffer+pos+4);
      if (off+len > length)
        return 0; /* out of bounds */
      if (len < 1)
        return 0; /* empty name */

      if (substr)
        {
          if (ascii_memcasemem (buffer+off, len, name, namelen))
            return idx+1; /* found */
        }
      else
        {
          if (len == namelen && !memcmp (buffer+off, name, len))
            return idx+1; /* found */
        }
    }
  return 0; /* not found */
}


main(int argc,char *argv[])
{
  struct srventry *srv;
  int rc,i;

  rc=getsrv("_hkp._tcp.wwwkeys.pgp.net",&srv);
  printf("Count=%d\n\n",rc);
  for(i=0;i<rc;i++)
    {
      printf("priority=%hu\n",srv[i].priority);
      printf("weight=%hu\n",srv[i].weight);
      printf("port=%hu\n",srv[i].port);
      printf("target=%s\n",srv[i].target);
      printf("\n");
    }

  xfree(srv);

  return 0;
}


inq_import_key_parms (void *opaque, const char *line)
{
  struct import_key_parm_s *parm = opaque;
  gpg_error_t err;

  if (has_leading_keyword (line, "KEYDATA"))
    {
      err = assuan_send_data (parm->dflt->ctx, parm->key, parm->keylen);
    }
  else
    err = default_inq_cb (parm->dflt, line);

  return err;
}


print_cipher_algo_note (cipher_algo_t algo)
{
  if(algo >= 100 && algo <= 110)
    {
      static int warn=0;
      if(!warn)
	{
	  warn=1;
          es_fflush (es_stdout);
	  log_info (_("WARNING: using experimental cipher algorithm %s\n"),
                    openpgp_cipher_algo_name (algo));
	}
    }
}


ccid_shutdown_reader (ccid_driver_t handle)
{
  int rc = 0;
  struct usb_device *dev = NULL;
  usb_dev_handle *idev = NULL;
  unsigned char *ifcdesc_extra = NULL;
  size_t ifcdesc_extra_len;
  int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr;

  if (!handle || !handle->rid)
    return CCID_DRIVER_ERR_INV_VALUE;

  do_close_reader (handle);

  if (scan_or_find_devices (-1, handle->rid, NULL, &dev,
                            &ifcdesc_extra, &ifcdesc_extra_len,
                            &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr,
                            &idev, NULL) || !idev)
    {
      DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid);
      return CCID_DRIVER_ERR_NO_READER;
    }

  if (idev)
    {
      handle->idev = idev;
      handle->ifc_no = ifc_no;
      handle->ep_bulk_out = ep_bulk_out;
      handle->ep_bulk_in = ep_bulk_in;
      handle->ep_intr = ep_intr;

      if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len))
        {
          DEBUGOUT ("device not supported\n");
          rc = CCID_DRIVER_ERR_NO_READER;
          goto leave;
        }

      rc = usb_claim_interface (idev, ifc_no);
      if (rc)
        {
          DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
          rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
          goto leave;
        }
    }

 leave:
  free (ifcdesc_extra);
  if (rc)
    {
      if (handle->idev)
        usb_close (handle->idev);
      handle->idev = NULL;
      if (handle->dev_fd != -1)
        close (handle->dev_fd);
      handle->dev_fd = -1;
    }

  return rc;

}


unhexify_fpr (const char *hexstr, unsigned char *fpr)
{
  const char *s;
  int n;

  for (s=hexstr, n=0; hexdigitp (s); s++, n++)
    ;
  if (*s || (n != 40))
    return 0; /* no fingerprint (invalid or wrong length). */
  for (s=hexstr, n=0; *s; s += 2, n++)
    fpr[n] = xtoi_2 (s);
  return 1; /* okay */
}


close_pcsc_reader (int slot)
{
#ifdef NEED_PCSC_WRAPPER
  return close_pcsc_reader_wrapped (slot);
#else
  return close_pcsc_reader_direct (slot);
#endif
}


checksum( byte *p, unsigned n )
{
    u16 a;

    for(a=0; n; n-- )
	a += *p++;
    return a;
}


parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
{
  byte *p;

  /* Cap comment packet at a reasonable value to avoid an integer
     overflow in the malloc below.  Comment packets are actually not
     anymore define my OpenPGP and we even stopped to use our
     private comment packet.  */
  if (pktlen > MAX_COMMENT_PACKET_LENGTH)
    {
      log_error ("packet(%d) too large\n", pkttype);
      if (list_mode)
        es_fprintf (listfp, ":%scomment packet: [too large]\n",
                    pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : "");
      iobuf_skip_rest (inp, pktlen, 0);
      return GPG_ERR_INV_PACKET;
    }
  packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1);
  packet->pkt.comment->len = pktlen;
  p = packet->pkt.comment->data;
  for (; pktlen; pktlen--, p++)
    *p = iobuf_get_noeof (inp);

  if (list_mode)
    {
      int n = packet->pkt.comment->len;
      es_fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT ?
                  "OpenPGP draft " : "");
      for (p = packet->pkt.comment->data; n; p++, n--)
	{
	  if (*p >= ' ' && *p <= 'z')
	    es_putc (*p, listfp);
	  else
	    es_fprintf (listfp, "\\x%02x", *p);
	}
      es_fprintf (listfp, "\"\n");
    }
  return 0;
}


write_sign_packet_header (IOBUF out, int ctb, u32 len)
{
  (void)ctb;

  /* Work around a bug in the pgp read function for signature packets,
     which are not correctly coded and silently assume at some point 2
     byte length headers.*/
  iobuf_put (out, 0x89 );
  iobuf_put (out, len >> 8 );
  return iobuf_put (out, len) == -1 ? -1:0;
}