call_get_cert_callback (gnutls_session_t session,
                        const gnutls_datum_t * issuers_dn,
                        int issuers_dn_length,
                        gnutls_pk_algorithm_t * pk_algos, int pk_algos_length)
{
  unsigned i;
  gnutls_pcert_st *local_certs = NULL;
  gnutls_privkey_t local_key = NULL;
  int ret = GNUTLS_E_INTERNAL_ERROR;
  gnutls_certificate_type_t type = gnutls_certificate_type_get (session);
  gnutls_certificate_credentials_t cred;
  gnutls_retr2_st st2;
  gnutls_pcert_st *pcert = NULL;
  unsigned int pcert_length = 0;

  cred = (gnutls_certificate_credentials_t)
    _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL);
  if (cred == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
    }

  memset (&st2, 0, sizeof (st2));

  if (cred->get_cert_callback2)
    {
      /* we avoid all allocations and transformations */
      ret = cred->get_cert_callback2 (session, issuers_dn, issuers_dn_length,
                                      pk_algos, pk_algos_length,
                                      &pcert, &pcert_length, &local_key);
      if (ret < 0)
        return gnutls_assert_val (GNUTLS_E_USER_ERROR);

      if (pcert_length > 0 && type != pcert[0].type)
        return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);

      if (pcert_length == 0)
        {
          pcert = NULL;
          local_key = NULL;
        }
      _gnutls_selected_certs_set (session, pcert, pcert_length, local_key, 0);

      return 0;

    }
  else if (cred->get_cert_callback)
    {
      ret = cred->get_cert_callback (session, issuers_dn, issuers_dn_length,
                                     pk_algos, pk_algos_length, &st2);

    }
  else
    {                           /* compatibility mode */
      gnutls_retr_st st;
      memset (&st, 0, sizeof (st));
      if (session->security_parameters.entity == GNUTLS_SERVER)
        {
          if (cred->server_get_cert_callback == NULL)
            {
              gnutls_assert ();
              return GNUTLS_E_INTERNAL_ERROR;
            }
          ret = cred->server_get_cert_callback (session, &st);
          if (ret >= 0)
            st_to_st2 (&st2, &st);
        }
      else
        {                       /* CLIENT */

          if (cred->client_get_cert_callback == NULL)
            {
              gnutls_assert ();
              return GNUTLS_E_INTERNAL_ERROR;
            }
          ret = cred->client_get_cert_callback (session,
                                                issuers_dn, issuers_dn_length,
                                                pk_algos, pk_algos_length,
                                                &st);
          if (ret >= 0)
            st_to_st2 (&st2, &st);
        }
    }

  if (ret < 0)
    {
      gnutls_assert ();
      return GNUTLS_E_USER_ERROR;
    }

  if (st2.ncerts == 0)
    return 0;                   /* no certificate was selected */

  if (type != st2.cert_type)
    {
      gnutls_assert ();
      ret = GNUTLS_E_INVALID_REQUEST;
      goto cleanup;
    }


  if (type == GNUTLS_CRT_X509)
    {
      local_certs = alloc_and_load_x509_certs (st2.cert.x509, st2.ncerts);
    }
  else
    {                           /* PGP */
      if (st2.ncerts > 1)
        {
          gnutls_assert ();
          ret = GNUTLS_E_INVALID_REQUEST;
          goto cleanup;
        }
#ifdef ENABLE_OPENPGP
      {
        local_certs = alloc_and_load_pgp_certs (st2.cert.pgp);
      }
#else
      ret = GNUTLS_E_UNIMPLEMENTED_FEATURE;
      goto cleanup;
#endif
    }

  if (local_certs == NULL)
    {
      gnutls_assert ();
      ret = GNUTLS_E_MEMORY_ERROR;
      goto cleanup;
    }

  switch (st2.key_type)
    {
    case GNUTLS_PRIVKEY_OPENPGP:
#ifdef ENABLE_OPENPGP
      if (st2.key.pgp != NULL)
        {
          local_key = alloc_and_load_pgp_key (st2.key.pgp, st2.deinit_all);
          if (local_key == NULL)
            {
              gnutls_assert ();
              ret = GNUTLS_E_INTERNAL_ERROR;
              goto cleanup;
            }
        }
#endif
      break;
    case GNUTLS_PRIVKEY_PKCS11:
#ifdef ENABLE_PKCS11
      if (st2.key.pkcs11 != NULL)
        {
          local_key =
            alloc_and_load_pkcs11_key (st2.key.pkcs11, st2.deinit_all);
          if (local_key == NULL)
            {
              gnutls_assert ();
              ret = GNUTLS_E_INTERNAL_ERROR;
              goto cleanup;
            }
        }
#endif
      break;
    case GNUTLS_PRIVKEY_X509:
      if (st2.key.x509 != NULL)
        {
          local_key = alloc_and_load_x509_key (st2.key.x509, st2.deinit_all);
          if (local_key == NULL)
            {
              gnutls_assert ();
              ret = GNUTLS_E_INTERNAL_ERROR;
              goto cleanup;
            }
        }
      break;
    default:
      gnutls_assert();
      ret = GNUTLS_E_INVALID_REQUEST;
      goto cleanup;
    }

  _gnutls_selected_certs_set (session, local_certs,
                              (local_certs != NULL) ? st2.ncerts : 0,
                              local_key, 1);

  ret = 0;

cleanup:

  if (st2.cert_type == GNUTLS_CRT_X509)
    {
      if (st2.deinit_all)
        {
          for (i = 0; i < st2.ncerts; i++)
            {
              gnutls_x509_crt_deinit (st2.cert.x509[i]);
            }
          gnutls_free(st2.cert.x509);
        }
    }
  else
    {
#ifdef ENABLE_OPENPGP
      if (st2.deinit_all)
        {
          gnutls_openpgp_crt_deinit (st2.cert.pgp);
        }
#endif
    }

  if (ret < 0)
    {
      if (local_key != NULL)
        gnutls_privkey_deinit (local_key);
    }

  return ret;
}
call_get_cert_callback (gnutls_session_t session,
                        const gnutls_datum_t * issuers_dn,
                        int issuers_dn_length,
                        gnutls_pk_algorithm_t * pk_algos, int pk_algos_length)
{
  unsigned i;
  gnutls_pcert_st *local_certs = NULL;
  gnutls_privkey_t local_key = NULL;
  int ret = GNUTLS_E_INTERNAL_ERROR;
  gnutls_certificate_type_t type = gnutls_certificate_type_get (session);
  gnutls_certificate_credentials_t cred;
  gnutls_retr2_st st2;
  gnutls_pcert_st *pcert = NULL;
  unsigned int pcert_length = 0;

  cred = (gnutls_certificate_credentials_t)
    _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL);
  if (cred == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
    }

  memset (&st2, 0, sizeof (st2));

  if (cred->get_cert_callback2)
    {
       
      ret = cred->get_cert_callback2 (session, issuers_dn, issuers_dn_length,
                                      pk_algos, pk_algos_length,
                                      &pcert, &pcert_length, &local_key);
      if (ret < 0)
        return gnutls_assert_val (GNUTLS_E_USER_ERROR);

      if (pcert_length > 0 && type != pcert[0].type)
        return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);

      if (pcert_length == 0)
        {
          pcert = NULL;
          local_key = NULL;
        }
      _gnutls_selected_certs_set (session, pcert, pcert_length, local_key, 0);

      return 0;

    }
  else if (cred->get_cert_callback)
    {
      ret = cred->get_cert_callback (session, issuers_dn, issuers_dn_length,
                                     pk_algos, pk_algos_length, &st2);

    }
  else
    {                            
      gnutls_retr_st st;
      memset (&st, 0, sizeof (st));
      if (session->security_parameters.entity == GNUTLS_SERVER)
        {
          if (cred->server_get_cert_callback == NULL)
            {
              gnutls_assert ();
              return GNUTLS_E_INTERNAL_ERROR;
            }
          ret = cred->server_get_cert_callback (session, &st);
          if (ret >= 0)
            st_to_st2 (&st2, &st);
        }
      else
        {                        

          if (cred->client_get_cert_callback == NULL)
            {
              gnutls_assert ();
              return GNUTLS_E_INTERNAL_ERROR;
            }
          ret = cred->client_get_cert_callback (session,
                                                issuers_dn, issuers_dn_length,
                                                pk_algos, pk_algos_length,
                                                &st);
          if (ret >= 0)
            st_to_st2 (&st2, &st);
        }
    }

  if (ret < 0)
    {
      gnutls_assert ();
      return GNUTLS_E_USER_ERROR;
    }

  if (st2.ncerts == 0)
    return 0;                    

  if (type != st2.cert_type)
    {
      gnutls_assert ();
      ret = GNUTLS_E_INVALID_REQUEST;
      goto cleanup;
    }


  if (type == GNUTLS_CRT_X509)
    {
      local_certs = alloc_and_load_x509_certs (st2.cert.x509, st2.ncerts);
    }
  else
    {                            
      if (st2.ncerts > 1)
        {
          gnutls_assert ();
          ret = GNUTLS_E_INVALID_REQUEST;
          goto cleanup;
        }
#ifdef ENABLE_OPENPGP
      {
        local_certs = alloc_and_load_pgp_certs (st2.cert.pgp);
      }
#else
      ret = GNUTLS_E_UNIMPLEMENTED_FEATURE;
      goto cleanup;
#endif
    }

  if (local_certs == NULL)
    {
      gnutls_assert ();
      ret = GNUTLS_E_MEMORY_ERROR;
      goto cleanup;
    }

  switch (st2.key_type)
    {
    case GNUTLS_PRIVKEY_OPENPGP:
#ifdef ENABLE_OPENPGP
      if (st2.key.pgp != NULL)
        {
          local_key = alloc_and_load_pgp_key (st2.key.pgp, st2.deinit_all);
          if (local_key == NULL)
            {
              gnutls_assert ();
              ret = GNUTLS_E_INTERNAL_ERROR;
              goto cleanup;
            }
        }
#endif
      break;
    case GNUTLS_PRIVKEY_PKCS11:
#ifdef ENABLE_PKCS11
      if (st2.key.pkcs11 != NULL)
        {
          local_key =
            alloc_and_load_pkcs11_key (st2.key.pkcs11, st2.deinit_all);
          if (local_key == NULL)
            {
              gnutls_assert ();
              ret = GNUTLS_E_INTERNAL_ERROR;
              goto cleanup;
            }
        }
#endif
      break;
    case GNUTLS_PRIVKEY_X509:
      if (st2.key.x509 != NULL)
        {
          local_key = alloc_and_load_x509_key (st2.key.x509, st2.deinit_all);
          if (local_key == NULL)
            {
              gnutls_assert ();
              ret = GNUTLS_E_INTERNAL_ERROR;
              goto cleanup;
            }
        }
      break;
    default:
      gnutls_assert();
      ret = GNUTLS_E_INVALID_REQUEST;
      goto cleanup;
    }

  _gnutls_selected_certs_set (session, local_certs,
                              (local_certs != NULL) ? st2.ncerts : 0,
                              local_key, 1);

  ret = 0;

cleanup:

  if (st2.cert_type == GNUTLS_CRT_X509)
    {
      if (st2.deinit_all)
        {
          for (i = 0; i < st2.ncerts; i++)
            {
              gnutls_x509_crt_deinit (st2.cert.x509[i]);
            }
          gnutls_free(st2.cert.x509);
        }
    }
  else
    {
#ifdef ENABLE_OPENPGP
      if (st2.deinit_all)
        {
          gnutls_openpgp_crt_deinit (st2.cert.pgp);
        }
#endif
    }

  if (ret < 0)
    {
      if (local_key != NULL)
        gnutls_privkey_deinit (local_key);
    }

  return ret;
}