/* v3_icrld.c */

#include <stdio.h>
#include "cryptlib.h"
#include <openssl/conf.h>
#include <openssl/asn1.h>
#include <openssl/asn1_mac.h>
#include <openssl/x509v3.h>

static STACK *i2v_idp(X509V3_EXT_METHOD *method, ISSUING_DIST_POINT *idp,
                                                         STACK *extlist);
static ISSUING_DIST_POINT *v2i_idp(X509V3_EXT_METHOD *method,
                                                 X509V3_CTX *ctx, STACK *nval);

static BIT_STRING_BITNAME some_reasons[] = {
{0, "unused", "unused"},
{1, "Key Compromise", "keyCompromise"},
{2, "CA Compromise", "CACompromise"},
{3, "Affiliation Changed", "affiliationChanged"},
{4, "Superseded", "superseded"},
{5, "Cessation Of Operation", "cessationOfOperation"},
{6, "Certificate Hold", "certificateHold"},
{-1, NULL, NULL}
};

X509V3_EXT_METHOD v3_idp = {
NID_issuing_distribution_points, X509V3_EXT_MULTILINE,
(X509V3_EXT_NEW)ISSUING_DIST_POINT_new,
ISSUING_DIST_POINT_free,
(X509V3_EXT_D2I)d2i_ISSUING_DIST_POINT,
i2d_ISSUING_DIST_POINT,
NULL, NULL,
(X509V3_EXT_I2V)i2v_idp,
(X509V3_EXT_V2I)v2i_idp,
NULL, NULL, (char *)some_reasons
};

static ISSUING_DIST_POINT *v2i_idp(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, STACK *nval)
{
	ISSUING_DIST_POINT *idp=NULL;
	STACK_OF(GENERAL_NAME) *gens = NULL;
	GENERAL_NAME *gen = NULL;
	CONF_VALUE *val, *vval;
	STACK *pval;
	int i;

	if(!(idp = ISSUING_DIST_POINT_new())) goto merr;

	for(i=0; i < sk_num(nval); i++) {
		val = (CONF_VALUE *)sk_value(nval, i);
		if(!strcmp(val->name, "distributionPoint")) {
			pval = (STACK *)X509V3_parse_list(val->value);
			vval = (CONF_VALUE *)sk_value(pval, 0);
			if(!(gen = v2i_GENERAL_NAME(method, ctx, vval))) goto err;
			if(!(gens = GENERAL_NAMES_new())) goto merr;
			if(!sk_GENERAL_NAME_push(gens, gen)) goto merr;
			gen = NULL;
			if(!(idp->distributionPoint = DIST_POINT_NAME_new())) goto merr;
			idp->distributionPoint->fullname = gens;
		} else if(!strcmp(val->name, "onlyContainsUserCerts")) {
			if(!X509V3_get_value_bool(val, &idp->onlyContainsUserCerts)) goto err;
		} else if (!strcmp(val->name, "onlyContainsCACerts")) {
			if(!X509V3_get_value_bool(val, &idp->onlyContainsCACerts)) goto err;
		} else if (!strcmp(val->name, "onlySomeReasons")) {
			pval = (STACK *)X509V3_parse_list(val->value);
			vval = (CONF_VALUE *)sk_value(pval, 0);
			if(!v2i_ASN1_BIT_STRING(method, ctx, vval)) goto err;
		} else if (!strcmp(val->name, "indirectCRL")) {
			if(!X509V3_get_value_bool(val, &idp->indirectCRL)) goto err;
		}
	}
	return idp;

merr:
	X509V3err(X509V3_F_V2I_ISSUING_DIST_POINT, ERR_R_MALLOC_FAILURE);
err:
	GENERAL_NAME_free(gen);
	GENERAL_NAMES_free(gens);
	return NULL;
}

static STACK *i2v_idp(X509V3_EXT_METHOD *method, ISSUING_DIST_POINT *idp, STACK *extlist)
{
	if(idp->distributionPoint->fullname) {
		extlist = i2v_GENERAL_NAMES(NULL, idp->distributionPoint->fullname, extlist);
	}
	X509V3_add_value_bool("onlyContainsUserCerts", idp->onlyContainsUserCerts, &extlist);
	X509V3_add_value_bool("onlyContainsCACerts", idp->onlyContainsCACerts, &extlist);
	if(idp->onlySomeReasons) {
		extlist = i2v_ASN1_BIT_STRING(method, idp->onlySomeReasons, extlist);
	}
	X509V3_add_value_bool("indirectCRL", idp->indirectCRL, &extlist);
	return extlist;
}

int i2d_ISSUING_DIST_POINT(ISSUING_DIST_POINT *a, unsigned char **pp)
{
	int v=0;

	M_ASN1_I2D_vars(a);

	M_ASN1_I2D_len_EXP_opt(a->distributionPoint, i2d_DIST_POINT_NAME, 0, v);
	M_ASN1_I2D_len_IMP_opt(a->onlyContainsUserCerts, i2d_ASN1_BOOLEAN);
	M_ASN1_I2D_len_IMP_opt(a->onlyContainsCACerts, i2d_ASN1_BOOLEAN);
	M_ASN1_I2D_len_IMP_opt(a->onlySomeReasons, i2d_ASN1_BIT_STRING);
	M_ASN1_I2D_len_IMP_opt(a->indirectCRL, i2d_ASN1_BOOLEAN);

	M_ASN1_I2D_seq_total();

	M_ASN1_I2D_put_EXP_opt(a->distributionPoint, i2d_DIST_POINT_NAME, 0, v);
	M_ASN1_I2D_put_IMP_opt(a->onlyContainsUserCerts, i2d_ASN1_BOOLEAN, 1);
	M_ASN1_I2D_put_IMP_opt(a->onlyContainsCACerts, i2d_ASN1_BOOLEAN, 2);
	M_ASN1_I2D_put_IMP_opt(a->onlySomeReasons, i2d_ASN1_BIT_STRING, 3);
	M_ASN1_I2D_put_IMP_opt(a->indirectCRL, i2d_ASN1_BOOLEAN, 4);

	M_ASN1_I2D_finish();
}

ISSUING_DIST_POINT *ISSUING_DIST_POINT_new(void)
{
	ISSUING_DIST_POINT *ret=NULL;
	ASN1_CTX c;

	M_ASN1_New_Malloc(ret, ISSUING_DIST_POINT);
	ret->distributionPoint = NULL;
	ret->onlyContainsUserCerts = 0; /* default is 'FALSE' */
	ret->onlyContainsCACerts = 0;
	ret->onlySomeReasons = NULL;
	ret->indirectCRL = 0;
	return (ret);
	M_ASN1_New_Error(ASN1_F_ISSUING_DIST_POINT_NEW);
}

ISSUING_DIST_POINT *d2i_ISSUING_DIST_POINT(ISSUING_DIST_POINT **a,
             unsigned char **pp, long length)
{
	M_ASN1_D2I_vars(a, ISSUING_DIST_POINT *, ISSUING_DIST_POINT_new);
	M_ASN1_D2I_Init();
	M_ASN1_D2I_start_sequence();
	M_ASN1_D2I_get_EXP_opt(ret->distributionPoint, d2i_DIST_POINT_NAME, 0);
	M_ASN1_D2I_get_IMP_opt(ret->onlyContainsUserCerts, d2i_ASN1_BOOLEAN, 1, V_ASN1_BOOLEAN);
	M_ASN1_D2I_get_IMP_opt(ret->onlyContainsCACerts, d2i_ASN1_BOOLEAN, 2, V_ASN1_BOOLEAN);
	M_ASN1_D2I_get_IMP_opt(ret->onlySomeReasons, d2i_ASN1_BIT_STRING, 3, V_ASN1_BIT_STRING);
	M_ASN1_D2I_get_IMP_opt(ret->indirectCRL, d2i_ASN1_BOOLEAN, 4, V_ASN1_BOOLEAN);
	M_ASN1_D2I_Finish(a, ISSUING_DIST_POINT_free, ASN1_F_D2I_ISSUING_DIST_POINT);
}

void ISSUING_DIST_POINT_free(ISSUING_DIST_POINT *a)
{
	if (a == NULL) return;
	DIST_POINT_NAME_free(a->distributionPoint);
	ASN1_BIT_STRING_free(a->onlySomeReasons);
	Free ((char *)a);
}


