/*************************************************************************
	> File Name: cert_session.c
	> Author:
	> Mail:
	> Created Time: Fri 01 Jun 2018 02:00:56 AM PDT
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <uuid/uuid.h>

#include "rt_string.h"
#include "rt_common.h"
#include "rt_stdlib.h"
#include "rt_time.h"
#include "rt_tmr.h"
#include "json.h"

/* openssl**/
#include <openssl/opensslv.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/crypto.h>
#include <openssl/engine.h>
#include <openssl/pkcs12.h>
#include <openssl/rsa.h>

#include <event2/listener.h>
#include <event2/http.h>
#include <event2/buffer.h>

#include <async.h>
#include <MESA/maat.h>
#include <MESA/field_stat2.h>
#include <MESA/MESA_prof_load.h>
#include <MESA/cJSON.h>

#include "cert_store.h"
#include "libevent.h"
#include "cert_session.h"
#include "logging.h"

#define	WAIT_FOR_EFFECTIVE_US	1000*1000

#define	SG_DATA_SIZE    10240

#define LOCAL_USER_PEN     1
#define LOCAL_USER_DER     2
#define LOCAL_USER_P12     3

#define	CM_UPDATE_TYPE_FULL	1
#define	CM_UPDATE_TYPE_INC	2

static x509_forge_thread *threads;

enum keypair_action
{
	KEYPAIR_ACTION_REQ = 0,
	KEYPAIR_ACTION_SQL,
	KEYPAIR_ACTION_SIGN,
	KEYPAIR_ACTION_ERR,
	KEYPAIR_ACTION_MAX
};

struct fs_stats_t{
	int field_id[KEYPAIR_ACTION_MAX];
    int line_ids[KEYPAIR_ACTION_MAX];
    screen_stat_handle_t handle;
	char histogram_bins[256];
	enum field_calc_algo favorite;
};

static struct fs_stats_t g_FP_instance = {
	.field_id = {0},
    .line_ids = {0},
    .handle = NULL,
    .histogram_bins = {0},
};

static const char* FP_HISTOGRAM_BINS="0.50,0.80,0.9,0.95,0.99";

#define sizeof_seconds(x)  (x * 24 * 60 * 60)
#define half_hours(x)    (x * 1800)

void connectCallback(const struct redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Redis connect error : %s", c->errstr);
        return;
    }
    mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Redis server connected...");
}

void disconnectCallback(const struct redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Redis disconnect error: %s", c->errstr);
        return;
    }
    mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Redis server disconnected...");
}

void x509_get_private_key(EVP_PKEY *pkey, char *pubkey)
{
	BIO *bp = NULL;
    int len = 0;

    if ( (bp=BIO_new(BIO_s_mem())) == NULL){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "unable to create BIO for output");
        goto finish;
    }
    PEM_write_bio_PrivateKey(bp, pkey, NULL, NULL, 0, NULL, NULL);
    len = BIO_read(bp, pubkey, SG_DATA_SIZE);
    if(len <= 0) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error reading signature file");
        goto free_err;
    }
    pubkey[len] = '\0';

free_err:
    BIO_free(bp);
finish:
	return;
}

#define R_RSA_ALGO_1024 1024
#define R_RSA_ALGO_2048 2048
#define R_RSA_ALGO_4096 4096
#define R_DH_ALGO_1024 1

typedef struct {
    const char *name;           /* NIST Name of curve */
    int nid;                    /* Curve NID */
} x509_algo_name;

static x509_algo_name algo_name[] = {
    {"rsa1024", R_RSA_ALGO_1024},
    {"rsa2048", R_RSA_ALGO_2048},
    {"rsa4096", R_RSA_ALGO_4096},
    {"secp256r1", NID_X9_62_prime256v1},
    {"secp384r1",NID_secp384r1}
};

static void fp_stat_latency(struct timespec create_time, int keys)
{
	struct timespec end;
	long long jiffies_ms=0;
	clock_gettime(CLOCK_MONOTONIC,&end);

	FS_operate(g_FP_instance.handle, g_FP_instance.line_ids[keys], 0,  FS_OP_ADD, 1);
	jiffies_ms=(end.tv_sec-create_time.tv_sec)*1000000+(end.tv_nsec-create_time.tv_nsec)/1000;
	FS_operate(g_FP_instance.handle, g_FP_instance.field_id[keys], 0, FS_OP_SET, jiffies_ms);
	FS_operate(g_FP_instance.handle, g_FP_instance.field_id[KEYPAIR_ACTION_REQ], 0, FS_OP_SET, jiffies_ms);

	return;
}

static size_t x509_algo_str2idx(const char *public_algo)
{
	size_t i = 0;

	if(public_algo == NULL)
	{
		goto finish;
	}

	if (0 == strcasecmp(public_algo, "dh1024"))
	{
		return R_DH_ALGO_1024;
	}

	for (i = 0; i < sizeof(algo_name) / sizeof(x509_algo_name); i++)
	{
		if (0 == strcasecmp(public_algo, algo_name[i].name))
		{
			return algo_name[i].nid;
		}
	}

finish:
	return R_RSA_ALGO_2048;
}

static int ssl_key_gen_rsa(EVP_PKEY** pkey, int nid)
{
    RSA *rsa = NULL;
    EVP_PKEY *pk = NULL;

    if((pk = EVP_PKEY_new()) == NULL){
        mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "ssl_key_gen_rsa, gen new key failed!");
        goto err;
    }

    rsa = RSA_generate_key(nid, RSA_F4, NULL, NULL);
    if(!EVP_PKEY_assign_RSA(pk, rsa)){
        mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "ssl_key_gen_rsa, assign key failed!");
        EVP_PKEY_free(pk);
        goto err;
    }
	rsa = NULL;
	*pkey = pk;
    return 1;
err:
    return 0;
}

int ssl_key_gen_dh(EVP_PKEY** pkey, int nid)
{
	EVP_PKEY *pk = NULL;

	DH *dh = DH_new();
	if(dh == NULL)
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "create dh key faild");
		goto error;
	}
#if 0
    dh = DH_generate_parameters(nid, DH_GENERATOR_2, NULL, NULL);
    if (dh == NULL)
    {
        return 0;
    }
    DH_generate_key(dh);
#endif
	if (!DH_generate_parameters_ex(dh, nid, DH_GENERATOR_2, NULL))
	{
        goto error;
    }
	if (!DH_generate_key(dh))
	{
		goto error;
	}

	if((pk = EVP_PKEY_new()) == NULL){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "ssl_key_gen_ecc, gen new key failed!");
        goto error;
    }
	if(!EVP_PKEY_assign_DH(pk, dh)){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "ssl_key_gen_ecc, assign key failed!");
        EVP_PKEY_free(pk);
        goto error;
    }
	*pkey = pk;
	return 1;
error:
	if(dh!=NULL)
		DH_free(dh);
	return 0;
}

int ssl_key_gen_ecc(EVP_PKEY** pkey, int nid)
{
	EC_GROUP *group = NULL;
	EVP_PKEY *pk = NULL;

	EC_KEY *eckey = EC_KEY_new();
	if(eckey == NULL)
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "create ec key faild");
		goto error;
	}

	/** Take an elliptic curve */
    group = EC_GROUP_new_by_curve_name(nid);
	if (group == NULL)
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "unable to create curve (%d)\n", nid);
		goto error;
	}
	EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
	EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);

	if (EC_KEY_set_group(eckey, group) == 0)
		goto error;

	if (!EC_KEY_generate_key(eckey))
	{
        goto error;
    }
	if((pk = EVP_PKEY_new()) == NULL){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "ssl_key_gen_ecc, gen new key failed!");
        goto error;
    }
    if(!EVP_PKEY_assign_EC_KEY(pk, eckey)){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "ssl_key_gen_ecc, assign key failed!");
        EVP_PKEY_free(pk);
        goto error;
    }
	eckey = NULL;
	*pkey = pk;
	return 1;

error:
	if(eckey!=NULL)
		EC_KEY_free(eckey);
	return 0;
}

static int ssl_key_gen(EVP_PKEY** pkey, char *pubkey, char *public_algo)
{
	int ret = 0, nid = 0;

	nid = x509_algo_str2idx(public_algo);
	switch(nid)
	{
		case R_RSA_ALGO_1024:
		case R_RSA_ALGO_2048:
		case R_RSA_ALGO_4096:
			ret = ssl_key_gen_rsa(pkey, nid);
			break;
		case NID_X9_62_prime256v1:
		case NID_secp384r1:
			ret = ssl_key_gen_ecc(pkey, nid);
			break;
 		case R_DH_ALGO_1024:
			ret = ssl_key_gen_dh(pkey, 1024);
		default:
			break;
	}

	if (ret != 1 || pkey == NULL)
		return 0;

	x509_get_private_key(*pkey, pubkey);

	return 1;
}

static X509* base_load_pkcs12(BIO *in, EVP_PKEY **pkey, X509 **x, STACK_OF(X509) **ca)
{
    PKCS12 *p12 = NULL;
    const char *pass = "";

    X509 *_x = NULL;
    EVP_PKEY *_pkey;
    STACK_OF(X509) *_ca = NULL;

    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();

    p12 = d2i_PKCS12_bio(in, NULL);
    if (p12 == NULL) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error loading PKCS12 file");
        goto finish;
    }
    if (!PKCS12_parse(p12, pass, &_pkey, &_x, &_ca)) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error parsing PKCS#12 file");
        goto finish;
    }

    if (x)
        *x = _x;
    if (pkey)
        *pkey = _pkey;
    if (ca)
        *ca = _ca;

 finish:
    if (p12)
        PKCS12_free(p12);
    return _x;
}

int x509_get_last_ca(char *file, X509 *cx509)
{
	int last = 0;
	X509 *x = NULL;
	BIO *bio = NULL;

	if ((bio = BIO_new(BIO_s_file())) == NULL)
	{
        goto finish;
    }
    if (BIO_read_filename(bio, file) <= 0)
	{
        goto finish;
    }
	while(NULL!=(x=PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL)))
	{
		if (0 == X509_NAME_cmp(X509_get_issuer_name(x), X509_get_subject_name(cx509)))
		{
			last = 1;
			X509_free(x);
			break;
		};
		X509_free(x);
	}
	BIO_free (bio);
finish:
	return last;
}

X509* x509_get_root_ca(char *file, int is_send, char *keyring_type, STACK_OF(X509) **stack_ca)
{
	int x509_cnt = 0;
	BIO *bio = NULL;
	STACK_OF(X509) *stack_x509 = NULL;
	X509 *certificate = NULL, *issuer = NULL, *caroot = NULL;

	if(!file){
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Input cert file is empty.");
		goto finish;
	}

	if ((bio = BIO_new(BIO_s_file())) == NULL) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Bio malloc failed.");
        goto finish;
    }
    if (BIO_read_filename(bio, file) <= 0) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error opening %s", file);
        goto finish;
    }
	if ((stack_x509 = sk_X509_new_null()) == NULL)
    {
        X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
        goto finish;
    }

	while(NULL!=(certificate=PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL)))
	{
		if (0 == X509_NAME_cmp(X509_get_issuer_name(certificate), X509_get_subject_name(certificate)))
		{
			/*This is caroot ca**/
			caroot = certificate;
			if (is_send == 0)
				continue;
		};
		/*This is last ca*/
		if (x509_get_last_ca(file, certificate) == 0)
		{
			issuer = certificate;
			if (strcasecmp(keyring_type, "end-entity") == 0)
			{
				continue;
			}
		}
		sk_X509_push(stack_x509, certificate);
		x509_cnt++;
	}
	if (x509_cnt >= 1)
        *stack_ca = stack_x509;
	if (issuer != NULL)
	{
		if (is_send == 0)
			X509_free(caroot);
	}
	else
	{
		issuer = caroot;
	}
	BIO_free (bio);
finish:
	return issuer;
}

EVP_PKEY * cert_base_key_x509 (BIO * bio, int iFormat, const char *strPwd)
{
    EVP_PKEY *pkey = NULL;

    switch (iFormat){
        case LOCAL_USER_PEN:
            pkey = PEM_read_bio_PrivateKey (bio, NULL, NULL, (char *)strPwd);
            break;
        case LOCAL_USER_P12:
            base_load_pkcs12(bio, &pkey, NULL, NULL);
            break;
        default:
            break;
    }

    return pkey;
}

EVP_PKEY * cert_load_key(char *keyfile)
{
    EVP_PKEY *pkey = NULL;
    BIO *in = NULL;

    if(!keyfile){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Input key file is empty.");
        goto finish;
    }
    if ((in = BIO_new(BIO_s_file())) == NULL) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Bio malloc failed.");
        goto finish;
    }
    if (BIO_read_filename(in, keyfile) <= 0) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error opening %s\n", keyfile);
        goto finish;
    }

    if ((pkey = cert_base_key_x509 (in, LOCAL_USER_PEN, "")) != NULL)
        goto finish;
    (void)BIO_reset (in);
    if ((pkey = cert_base_key_x509 (in, LOCAL_USER_P12, "")) != NULL)
           goto finish;
finish:
    if (in != NULL)
        BIO_free (in);
    return pkey;
}

void uuid_squeeze(char *s,int c)
{
    int i,j;
    for (i = 0, j = 0; s[i] != '\0'; i++)
    {
        if (s[i] != c)
        {
            s[j++] = s[i];
        }
    }
    s[j] = '\0';
}

int
ssl_x509_set_serial(ASN1_INTEGER *ai)
{
    int ret = -1;
    uuid_t uu;
    char buf[64] = {0};
    BIGNUM *bignum = NULL;

    uuid_generate(uu);
    uuid_unparse(uu, buf);
    uuid_squeeze(buf, '-');

    BN_hex2bn(&bignum, buf);

    if (ai && !BN_to_ASN1_INTEGER(bignum, ai))
        goto error;
    ret = 1;
error:
    if (bignum)
        BN_free(bignum);
    return ret;
}

/*
 * Add a X509v3 extension to a cert and handle errors.
 * Returns -1 on errors, 0 on success.
 */
int ssl_x509_v3ext_add(X509V3_CTX * ctx, X509 * crt, const char *k, const char *v)
{
	X509_EXTENSION * ext;

	if (!(ext = X509V3_EXT_conf(NULL, ctx, k, v)))
	{
		return -1;
	}
	if (X509_add_ext(crt, ext, -1) != 1)
	{
		X509_EXTENSION_free(ext);
		return -1;
	}
	X509_EXTENSION_free(ext);
	return 0;
}

int ssl_x509_v3ext_copy_by_nid(X509 *crt, X509 *origcrt, int nid)
{
	X509_EXTENSION *ext;
	int pos;

	pos = X509_get_ext_by_NID(origcrt, nid, -1);
	if (pos == -1)
		return 0;
	ext = X509_get_ext(origcrt, pos);
	if (!ext)
		return -1;

	if (X509_add_ext(crt, ext, -1) != 1)
		return -1;

	return 1;
}
/*
 * Add extension using V3 code: we can set the config file as NULL because we
 * wont reference any other sections.
 */

int add_ext(X509 *cacrt, X509 *cert, int nid, char *value)
{
    X509_EXTENSION *ex;
    X509V3_CTX ctx;
    /* This sets the 'context' of the extensions. */
    /* No configuration database */
    X509V3_set_ctx_nodb(&ctx);
    /*
     * Issuer and subject certs: both the target since it is self signed, no
     * request and no CRL
     */
    X509V3_set_ctx(&ctx, cacrt, cert, NULL, NULL, 0);
    ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
    if (!ex)
        return 0;

    X509_add_ext(cert, ex, -1);
    X509_EXTENSION_free(ex);
    return 1;
}

static time_t ASN1_GetTimeT(ASN1_TIME* time)
{
    struct tm t; time_t utc_timep;

    const char* str = (const char*) time->data;
    size_t i = 0;

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

    if (time->type == V_ASN1_UTCTIME) {/* two digit year */
        t.tm_year = (str[i++] - '0') * 10;
        t.tm_year += (str[i++] - '0');
        if (t.tm_year < 70)
            t.tm_year += 100;
    } else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
        t.tm_year = (str[i++] - '0') * 1000;
        t.tm_year+= (str[i++] - '0') * 100;
        t.tm_year+= (str[i++] - '0') * 10;
        t.tm_year+= (str[i++] - '0');
        t.tm_year -= 1900;
    }
    t.tm_mon  = (str[i++] - '0') * 10;
    t.tm_mon += (str[i++] - '0') - 1; // -1 since January is 0 not 1.
    t.tm_mday = (str[i++] - '0') * 10;
    t.tm_mday+= (str[i++] - '0');
    t.tm_hour = (str[i++] - '0') * 10;
    t.tm_hour+= (str[i++] - '0');
    t.tm_min  = (str[i++] - '0') * 10;
    t.tm_min += (str[i++] - '0');
    t.tm_sec  = (str[i++] - '0') * 10;
    t.tm_sec += (str[i++] - '0');

    /* Note: we did not adjust the time based on time zone information */
	setenv("TZ", "UTC", 1);
	utc_timep = mktime(&t);
	unsetenv("TZ");

	return utc_timep;
}

void x509_get_private_ecc_key(EC_KEY *key1, char *pubkey)
{
	BIO *bp = NULL;
    int len = 0;

    if ( (bp=BIO_new(BIO_s_mem())) == NULL){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "unable to create BIO for output");
        goto finish;
    }
	PEM_write_bio_ECPrivateKey(bp, key1, NULL, NULL, 0, NULL, NULL);
    len = BIO_read(bp, pubkey, SG_DATA_SIZE);
    if(len <= 0){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error reading signature file");
        goto free_err;
    }
    pubkey[len] = '\0';

free_err:
    BIO_free(bp);
finish:
	return;
}

X509 *ssl_x509_forge(X509 *cacrt, EVP_PKEY *cakey, X509 *origcrt, char *pkey, int *expire_time, char *crlurl, char *public_algo)
{
	int rv;
	X509 *crt = NULL;
    EVP_PKEY* key = NULL;
	X509_NAME *subject = NULL, *issuer = NULL;

    if(!ssl_key_gen(&key, pkey, public_algo)){
        goto err;
    }
	//subjectname,issuername
	subject = X509_get_subject_name(origcrt);
	issuer = X509_get_subject_name(cacrt);
	if (!subject || !issuer)
		return NULL;

	crt = X509_new();
	if (!crt)
		return NULL;
	//version,subjectname,issuername,serialnum,time,pubkey
	if (!X509_set_version(crt, 0x02) ||
	    !X509_set_subject_name(crt, subject) ||
	    !X509_set_issuer_name(crt, issuer) ||
	    ssl_x509_set_serial(X509_get_serialNumber(crt)) == -1 ||
	    !X509_set_pubkey(crt, key))
		goto errout;

	if (*expire_time <= 0)
	{
		int day = 0, sec = 0;
		ASN1_TIME_set(X509_get_notBefore(crt), ASN1_GetTimeT(X509_get_notBefore(origcrt)));
		ASN1_TIME_set(X509_get_notAfter(crt), ASN1_GetTimeT(X509_get_notAfter(origcrt)));
		ASN1_TIME_diff(&day, &sec, X509_get_notBefore(crt), X509_get_notAfter(crt));
		*expire_time = MIN(sizeof_seconds(day) + sec, sizeof_seconds(1));
	}
	else
	{
		if(!X509_gmtime_adj(X509_get_notBefore(crt), (long)(0 - half_hours(*expire_time))) ||
		   !X509_gmtime_adj(X509_get_notAfter(crt), (long)(half_hours(*expire_time))))
		{
			goto errout;
		}
		*expire_time = half_hours(*expire_time);
	}

    EVP_PKEY_free(key);
	key=NULL;

//extensions
	X509V3_CTX ctx;
	X509V3_set_ctx(&ctx, cacrt, crt, NULL, NULL, 0);

	if (ssl_x509_v3ext_add(&ctx, crt, "subjectKeyIdentifier",
	                                  "hash") == -1 ||
	    ssl_x509_v3ext_add(&ctx, crt, "authorityKeyIdentifier",
	                                  "keyid,issuer:always") == -1)
		goto errout;

	rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt,
	                                NID_basic_constraints);
	if (rv == 0)
		rv = ssl_x509_v3ext_add(&ctx, crt, "basicConstraints",
		                                   "CA:FALSE");
	if (rv == -1)
		goto errout;

	rv = ssl_x509_v3ext_add(&ctx, crt, "keyUsage",
		                               "digitalSignature,"
		                               "keyEncipherment");
	if (rv == -1)
		goto errout;

	rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt,
	                                NID_ext_key_usage);
	if (rv == 0)
		rv = ssl_x509_v3ext_add(&ctx, crt, "extendedKeyUsage",
		                                   "serverAuth");
	if (rv == -1)
		goto errout;

    if (crlurl != NULL && strcasecmp(crlurl, "null")){
        mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Sign certificate the CRL is %s", crlurl);
		char * crlurlval;
		if (asprintf(&crlurlval, "URI:%s", crlurl) < 0)
			goto errout;
		if (ssl_x509_v3ext_add(&ctx, crt, "crlDistributionPoints", crlurlval) == -1)
		{
			free(crlurlval);
			goto errout;
		}
		free(crlurlval);
    }
	/* no extraname provided: copy original subjectAltName ext */
	if (ssl_x509_v3ext_copy_by_nid(crt, origcrt,
	                               NID_subject_alt_name) == -1)
    {
		goto errout;
    }
#ifdef DEBUG_CERTIFICATE
	ssl_x509_v3ext_add(&ctx, crt, "nsComment", "Generated by " PKGLABEL);
#endif /* DEBUG_CERTIFICATE */

	const EVP_MD *md;
	switch (EVP_PKEY_type(EVP_PKEY_base_id(cakey))) {
#ifndef OPENSSL_NO_RSA
	case EVP_PKEY_RSA:
		switch (X509_get_signature_nid(origcrt)) {
		case NID_md5WithRSAEncryption:
			md = EVP_md5();
			break;
		case NID_ripemd160WithRSA:
			md = EVP_ripemd160();
			break;
		case NID_sha1WithRSAEncryption:
			md = EVP_sha1();
			break;
		case NID_sha224WithRSAEncryption:
			md = EVP_sha224();
			break;
		case NID_sha256WithRSAEncryption:
			md = EVP_sha256();
			break;
		case NID_sha384WithRSAEncryption:
			md = EVP_sha384();
			break;
		case NID_sha512WithRSAEncryption:
			md = EVP_sha512();
			break;
#ifndef OPENSSL_NO_SHA0
		case NID_shaWithRSAEncryption:
			md = EVP_sha();
			break;
#endif /* !OPENSSL_NO_SHA0 */
		default:
			md = EVP_sha256();
			break;
		}
		break;
#endif /* !OPENSSL_NO_RSA */
#ifndef OPENSSL_NO_DSA
	case EVP_PKEY_DSA:
		switch (X509_get_signature_nid(origcrt)) {
		case NID_dsaWithSHA1:
		case NID_dsaWithSHA1_2:
			md = EVP_sha1();
			break;
		case NID_dsa_with_SHA224:
			md = EVP_sha224();
			break;
		case NID_dsa_with_SHA256:
			md = EVP_sha256();
			break;
#ifndef OPENSSL_NO_SHA0
		case NID_dsaWithSHA:
			md = EVP_sha();
			break;
#endif /* !OPENSSL_NO_SHA0 */
		default:
			md = EVP_sha256();
			break;
		}
		break;
#endif /* !OPENSSL_NO_DSA */
#ifndef OPENSSL_NO_ECDSA
	case EVP_PKEY_EC:
		switch (X509_get_signature_nid(origcrt)) {
		case NID_ecdsa_with_SHA1:
			md = EVP_sha1();
			break;
		case NID_ecdsa_with_SHA224:
			md = EVP_sha224();
			break;
		case NID_ecdsa_with_SHA256:
			md = EVP_sha256();
			break;
		case NID_ecdsa_with_SHA384:
			md = EVP_sha384();
			break;
		case NID_ecdsa_with_SHA512:
			md = EVP_sha512();
			break;
		default:
			md = EVP_sha256();
			break;
		}
		break;
#endif /* !OPENSSL_NO_ECDSA */
	default:
		goto errout;
	}
	if (!X509_sign(crt, cakey, md))
		goto errout;

	return crt;
errout:
	X509_free(crt);
	if(key)
    	EVP_PKEY_free(key);
err:
	return NULL;
}

void x509_get_msg_from_ca(X509 *x509, char **root)
{
	BIO *bp = NULL;
    int len = 0;

    if ( (bp=BIO_new(BIO_s_mem())) == NULL){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "unable to create BIO for output");
        return;
    }
    PEM_write_bio_X509(bp, x509);

    char *p = NULL;
    len = BIO_get_mem_data(bp, &p);
    *root = (char*)malloc(len + 1);
    memset(*root, 0, len + 1);

    len = BIO_read(bp, *root, len);
    if(len <= 0) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error reading signature file");
        goto err;
    }
err:
    BIO_free(bp);
	return;
}

X509 *
x509_get_ca_from_msg(const char *cert, int len)
{
    BIO *bp;
    char *in = NULL;
    X509* x509 = NULL;

    in = (char *)kmalloc(len, MPF_CLR, -1);
    assert(in);

	strncpy(in, cert, len);

    if ( (bp=BIO_new(BIO_s_mem())) == NULL){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "unable to create BIO for output");
        goto finish;
    }
	BIO_printf(bp, "%s", in);
	x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
	if(NULL == x509) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to read pem file");
        goto err;
    }
err:
	BIO_free(bp);
finish:
    free(in);
	return x509;
}

void request_destroy(struct http_request *request)
{
	if (request->odata)
	{
		free(request->odata);
		request->odata=NULL;
	}
	if (request->sni)
	{
		free(request->sni);
		request->sni=NULL;
	}
	if (request->keyring_uuid_str)
	{
		free(request->keyring_uuid_str);
		request->keyring_uuid_str=NULL;
	}

	free(request);
	request = NULL;
}

static int redis_async_connect(struct event_base *base, struct redisAsyncContext **cl_ctx)
{
    *cl_ctx = redisAsyncConnect(g_cert_store->local_redis_ip, g_cert_store->local_redis_port);
	if((*cl_ctx)->err )
	{
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Redis Connect error : %s", (*cl_ctx)->errstr);
		return -1;
	}
	redisLibeventAttach((*cl_ctx), base);
	redisAsyncSetConnectCallback((*cl_ctx), connectCallback);
	redisAsyncSetDisconnectCallback((*cl_ctx), disconnectCallback);
	return 0;
}

static int evhttp_socket_send_error(struct evhttp_request *req, int error)
{
	FS_operate(g_FP_instance.handle, g_FP_instance.line_ids[KEYPAIR_ACTION_ERR], 0, FS_OP_ADD, 1);
	evhttp_send_error(req, error, 0);
	return 0;
}

/* Callback used for the /dump URI, and for every non-GET request:
 * dumps all information to stdout and gives back a trivial 200 ok */
static int evhttp_socket_send(struct evhttp_request *req, char *sendbuf)
{
    struct evbuffer *evb = NULL;

    /* This holds the content we're sending. */
	evb = evbuffer_new();

    if (sendbuf[0] == '\0' && req == NULL){
        goto err;
    }
    evhttp_add_header(evhttp_request_get_output_headers(req),
		              "Content-Type", "text/html");
	evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "keep-alive");
    evbuffer_add_printf(evb, "%s", sendbuf);
    evhttp_send_reply(req, HTTP_OK, "OK", evb);
    goto done;

err:
    evhttp_send_error(req, HTTP_NOTFOUND, "Document was not found");
done:
    evbuffer_free(evb);
    return 0;
}

static void redis_reget_callback(redisAsyncContext __attribute__((__unused__))*cl_ctx,
                     void *r, void *privdata)
{
    redisReply *reply              = (redisReply*)r;

    struct http_request *request      = (struct http_request *)privdata;

    struct evhttp_request *evh_req = request->evh_req;
    evhttp_socket_send(evh_req, reply->str);
	request_destroy(request);
    return;
}

void keyring_table_free_cb(const char *table_name, void **ad, long argl, void *argp)
{
    if (*ad == NULL)
   	{
        return;
	}
	struct pxy_obj_keyring* pxy_obj=(struct pxy_obj_keyring*)(*ad);
	atomic64_dec(&pxy_obj->ref_cnt);
    if (atomic64_read(&pxy_obj->ref_cnt) == 0)
    {
        if (pxy_obj->issuer)
            X509_free(pxy_obj->issuer);
        if (pxy_obj->key)
            EVP_PKEY_free(pxy_obj->key);

		if (pxy_obj->stack_ca)
			sk_X509_pop_free(pxy_obj->stack_ca, X509_free);

        free(pxy_obj);
        pxy_obj = NULL;
        *ad=NULL;
    }
}

void keyring_table_free(struct pxy_obj_keyring* pxy_obj)
{
    keyring_table_free_cb(0, (void **)&pxy_obj, 0, NULL);
}

int add_cert_ctx(X509_NAME* name, char* ctx[], int num)
{
    int i = 0;
    int max = 0;

    int item[] = {NID_commonName, NID_countryName,
        NID_stateOrProvinceName, NID_localityName,
        NID_organizationName, NID_organizationalUnitName,
        NID_pkcs9_emailAddress};

    max = sizeof(item)/sizeof(item[0]);
    max = max > num ? num : max;

    for(i = 0; i< max; ++i){
        if(!X509_NAME_add_entry_by_NID(name, item[i], MBSTRING_UTF8, (unsigned char *)ctx[i], -1, -1, 0)){
            mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "add_cert_ctx, add entry:%d to %s failed!", item[i], ctx[i]);
            return 0;
        }
    }

    return 1;
}

int rand_serial(BIGNUM *b, ASN1_INTEGER *ai)
{
#define SERIAL_RAND_BITS  124
    BIGNUM *btmp;
    int ret = 0;
    if (b)
        btmp = b;
    else
        btmp = BN_new();
    if (!btmp)
        return 0;
    if (!BN_pseudo_rand(btmp, SERIAL_RAND_BITS, 0, 0))
        goto error;
    if (ai && !BN_to_ASN1_INTEGER(btmp, ai))
        goto error;
    ret = 1;

 error:
    if (!b)
        BN_free(btmp);
    return ret;
}

char *x509_get_sn(X509 *x509)
{
	ASN1_INTEGER *asn1_i = NULL;
	BIGNUM *bignum = NULL;
	char *serial = NULL;

	asn1_i = X509_get_serialNumber(x509);
	bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
	if (bignum == NULL)	{
		goto finish;
	}
	serial = BN_bn2hex(bignum);
    if (serial == NULL)	{
		goto finish;
	}
    BN_free(bignum);
finish:
	return serial;
}

static struct pxy_obj_keyring* get_obj_for_id(char *keyring_uuid_str)
{
	struct pxy_obj_keyring *pxy_obj=NULL;
	pxy_obj = (struct pxy_obj_keyring*)maat_plugin_table_get_ex_data(g_cert_store->instance, "DECRYPTION_KEYRING", keyring_uuid_str, strlen(keyring_uuid_str));
	return pxy_obj;
}

static int x509_online_append(struct x509_object_ctx *def, struct http_request *request, char **sign, char *pkey,
                              STACK_OF(X509) **stack_ca)
{
    X509* x509 = NULL;
    int is_valid = 1; char* keyring_uuid_str = request->keyring_uuid_str;
    int expire_time = 0; char *serial = NULL;
    X509 *cacrt = NULL; EVP_PKEY *cakey = NULL;
	char *v3_ctl=NULL, *public_algo=NULL;

    struct pxy_obj_keyring *pxy_obj = get_obj_for_id(keyring_uuid_str);
    if (NULL == pxy_obj)
    {
        if (!g_cert_store->local_debug)
        {
            pxy_obj = get_obj_for_id(keyring_uuid_str);
			if (pxy_obj == NULL)
			{
				mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Get the default keypair failed, EXIT!!!");
				exit(0);
			}
			else
			{
				mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Get the keypar %s, sign cert", keyring_uuid_str);
			}
        }
        else
        {
            cacrt = (is_valid == 1) ? def->root : def->insec_root;
            cakey  = (is_valid == 1) ? def->key  : def->insec_key;
            expire_time = g_cert_store->expire_after;
            mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Warning: Use local keypair, sign cert!!!");
            goto modify;
        }
    }

	if(pxy_obj->use_hsm)
	{
		cacrt = (is_valid == 1) ? def->root : def->insec_root;
		cakey  = (is_valid == 1) ? def->key  : def->insec_key;
		expire_time = g_cert_store->expire_after;
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Warning: HSM is not supported, use local keypair, sign cert!!!");
		goto modify;
	}
    if (!STRCMP(pxy_obj->keyring_type, "end-entity"))
    {
        mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "The certificate(%s) type is an entity",keyring_uuid_str);
		*stack_ca = pxy_obj->stack_ca;
        x509_get_msg_from_ca(pxy_obj->issuer, sign);
        x509_get_private_key(pxy_obj->key, pkey);
        goto finish;
    }
    if (!STRCMP(pxy_obj->keyring_type, "intermediate"))
    {
        mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "The certificate(%s) type is intermediate", keyring_uuid_str);

    }
    cacrt = pxy_obj->issuer;
    cakey  = pxy_obj->key;
	v3_ctl = pxy_obj->v3_ctl;
	public_algo =pxy_obj->public_algo;
    expire_time = pxy_obj->expire_time;
	*stack_ca = pxy_obj->stack_ca;
modify:
    x509 = ssl_x509_forge(cacrt, cakey, request->origin, pkey, &expire_time, v3_ctl, public_algo);
    if (!x509){
        goto finish;
    }
    serial = x509_get_sn(x509);
    mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "The certificate serial number is %s", serial);
    OPENSSL_free(serial);

    x509_get_msg_from_ca(x509, sign);

    if (request->origin)
        X509_free(request->origin);
    X509_free(x509);
finish:
    if (pxy_obj)
        keyring_table_free(pxy_obj);
    return expire_time;
}

static char readBytes(char *str)
{
    char c = '+';

    if (str && STRCMP(str, "OK") == 0)
        c = '+';
    if (!str)
        c= '$';

    return c;
}

static void
redis_sync_reget_callback(struct http_request *request, struct redisContext *sync)
{
	struct evhttp_request *evh_req = request->evh_req;

	redisReply *reply = (redisReply *)redisCommand(sync, "GET %s", request->rkey);
	if (NULL == reply)
	{
		goto free;
	}
	switch (readBytes(reply->str))
	{
		case '+' :
		    evhttp_socket_send(evh_req, reply->str);
            break;
		default  :
			evhttp_send_error(request->evh_req, HTTP_NOTFOUND, 0);
			break;
	}
free:
	freeReplyObject(reply);
	request_destroy(request);
	return;
}

static int
rediSyncCommand(redisContext *sync, struct http_request *request, char *odata, int expire_after)
{
    int xret = -1;
    redisReply *reply;

    x509_forge_thread *thread        = threads + request->thread_id;
    struct evhttp_request *evh_req = request->evh_req;

    reply = (redisReply *)redisCommand(thread->sync, "set %s %s ex %d nx", request->rkey, odata, expire_after);
    if (NULL == reply)
        goto free;

    switch (readBytes(reply->str)) {
        case '+' :
            mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Writing data(%s) to redis successfully", request->rkey);
			fp_stat_latency(request->create_time, KEYPAIR_ACTION_SIGN);
            evhttp_socket_send(evh_req, request->odata);
            goto free;
        case '$' :
            mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Writing data(%s) to redis failed", request->rkey);
			fp_stat_latency(request->create_time, KEYPAIR_ACTION_SQL);
			if (g_cert_store->mode){
            	redisAsyncCommand(thread->cl_ctx, redis_reget_callback, request, "GET %s", request->rkey);
			}else{
				redis_sync_reget_callback(request, sync);
			}
            freeReplyObject(reply);
            goto finish;
        default:
            mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Read redis data(%s) return code failed", request->rkey);
			evhttp_socket_send_error(request->evh_req, HTTP_NOTFOUND);
            goto free;
    }
    xret = 0;

free:
    freeReplyObject(reply);
	request_destroy(request);
finish:
    return xret;
}

static inline json_object *
web_json_record_array_add_string(char **chain)
{
    int i;
    json_object *sample_array;

    sample_array = json_object_new_array();
    if (sample_array == NULL)
        goto finish;

    for(i = 0; chain[i] != NULL; i++){
        json_object_array_add(sample_array, json_object_new_string(chain[i]));
    }
finish:
    return sample_array;
}

static inline int
json_data_rebuild(const char *data,
                 size_t size,
                 char **odata,
                 size_t *osize)
{
    size_t real_size = size + 1; /** 2, '\n' + '\0' */

    if (!data || !size)
        return -1;

    *odata = (char *)malloc (real_size);
    if (!*odata)
        return -1;
    memset (*odata, 0, real_size);
    snprintf(*odata, real_size, "%s", data);

    *osize = real_size;

    return 0;
}

static int web_json_table_add(char *privatekey, char *sign, char **digital_certificates, char **data)
{
    int i = 0;
    size_t osize = 0;
    const char *jstr = NULL;
    struct json_object *outline = json_object_new_object();

    json_object_object_add(outline, "CERTIFICATE_CHAIN", web_json_record_array_add_string(digital_certificates));
	json_object_object_add(outline, "PRIVATE_KEY", json_object_new_string(privatekey));
    json_object_object_add(outline, "CERTIFICATE", json_object_new_string(sign));

    jstr = json_object_to_json_string (outline);

    json_data_rebuild(jstr, strlen(jstr), data, &osize);

    json_object_put(outline);

    kfree(sign);
    for (i = 0; i < 6; i ++){
        if (digital_certificates[i] != NULL)
            kfree(digital_certificates[i]);
    }
    return 0;
}

static int
redis_clnt_pdu_send(struct http_request *request)
{
#define MAX_CHAIN_LEN 6
    int xret = -1, i = 0;
    STACK_OF(X509) *stack_ca = NULL;
    x509_forge_thread *thread = threads + request->thread_id;
    char *sign = NULL, pkey[SG_DATA_SIZE] = {0};

    uint64_t expire_time = x509_online_append(&thread->def, request, &sign, pkey, &stack_ca);
    if (sign == NULL || pkey[0] == '\0')
	{
		if (request->origin)
        	X509_free(request->origin);
		request_destroy(request);
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to sign certificate");
		evhttp_socket_send_error(request->evh_req, HTTP_NOTFOUND);
		return xret;
    }

    char *certificate = NULL; char *digital_certificates[MAX_CHAIN_LEN] = {0};
    if (stack_ca)
	{
        for (i = 0; i < sk_X509_num(stack_ca); i++)
		{
            x509_get_msg_from_ca(sk_X509_value(stack_ca, i), &certificate);
            digital_certificates[i] = certificate;
        }
    }
    web_json_table_add(pkey, sign, digital_certificates, &request->odata);

    if (thread->sync == NULL)
	{
        struct evhttp_request *evh_req = request->evh_req;
		fp_stat_latency(request->create_time, KEYPAIR_ACTION_SIGN);
        evhttp_socket_send(evh_req, request->odata);
        request_destroy(request);
        xret = 0;
        goto finish;
    }
    xret = rediSyncCommand(thread->sync, request, request->odata, expire_time);
    if (xret < 0)
	{
        goto finish;
    }
    xret = 0;
finish:
    return xret;
}

static int redis_clnt_send(struct http_request *request, redisReply *reply)
{
    int xret                       = -1;

    if (!reply && !reply->str){
		evhttp_socket_send_error(request->evh_req, HTTP_NOTFOUND);
        goto finish;
    }
	fp_stat_latency(request->create_time, KEYPAIR_ACTION_SQL);
    evhttp_socket_send(request->evh_req, reply->str);

finish:
    if (request->origin)
        X509_free(request->origin);
    request_destroy(request);
    return xret;
}

void redis_get_callback(redisAsyncContext __attribute__((__unused__))*c, void *r, void *privdata)
{
    int __attribute__((__unused__))xret = -1;

	redisReply *reply              = (redisReply*)r;
    struct http_request *request      = (struct http_request *)privdata;

    switch(reply->type){
        case REDIS_REPLY_STRING:
            mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Sends the certificate information to the requestor");

            xret = redis_clnt_send(request, reply);
            break;

        case REDIS_REPLY_NIL:
            mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Generating certificate information");

            xret = redis_clnt_pdu_send(request);
            break;
        default:
            break;
    }
    return;
}

int x509_key_pair_init(char *ca_file, EVP_PKEY **key, X509 **root)
{
    int xret = -1;
	FILE *fp; RSA *rsa = NULL;

	*key = EVP_PKEY_new();
    if (NULL == *key){
        goto finish;
    }

    rsa = RSA_new();
    if (NULL == rsa){
        goto pkey_free;
    }

    fp = fopen(ca_file, "r");
    if (NULL == fp){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to open file(%s)", ca_file);
        RSA_free(rsa);
        goto pkey_free;
    }
    if ( !PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL) || !EVP_PKEY_assign_RSA(*key,rsa))
	{
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Private key read failed");
        goto pkey_free;
	}
    fclose(fp);

    BIO *in;
    in = BIO_new_file(ca_file, "r");
    if (!in){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to open file(%s)", ca_file);
        goto pkey_free;
    }

    if ((*root = PEM_read_bio_X509(in, NULL, 0, NULL)) == NULL )
    {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Application for x509 failed");
		goto pkey_free;
	}
    BIO_free(in);

    xret = 0;
    goto finish;

pkey_free:
    EVP_PKEY_free(*key);
finish:
    return xret;
}

int hex2dec(char c)
{
    if ('0' <= c && c <= '9') {
        return c - '0';
    } else if ('a' <= c && c <= 'f') {
        return c - 'a' + 10;
    } else if ('A' <= c && c <= 'F') {
        return c - 'A' + 10;
    } else {
        return -1;
    }
}

void _urldecode(char url[])
{
    int i = 0;
    int len = strlen(url);
    int res_len = 0;
    char *res = NULL;

    res = (char *)malloc(len + 1);
    if (!res){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Url alloc faild");
        return;
    }

    if(!strchr(url, '%'))
        return;

    for (i = 0; i < len; ++i) {
        char c = url[i];
        if (c != '%') {
            res[res_len++] = c;
        } else {
            char c1 = url[++i];
            char c0 = url[++i];
            int num = 0;
            num = hex2dec(c1) * 16 + hex2dec(c0);
            res[res_len++] = num;
        }
    }
    res[res_len] = '\0';
    strcpy(url, res);

    free(res);
}

int http_get_headers(struct evhttp_request *evh_req, struct evkeyvalq *headers)
{
	int xret = -1;

	const char *uri = evhttp_request_get_uri(evh_req);
	if(!uri)
	{
		return xret;
	}
	return evhttp_parse_query(uri, headers);
}

int http_get_heal_check(struct evhttp_request *evh_req)
{
	int xret = -1;
	struct evkeyvalq headers;
	xret = http_get_headers(evh_req, &headers);
	if(xret != 0)
	{
		return xret;
	}

	xret = (evhttp_find_header(&headers, "health_check") != NULL) ? 1 : 0;
	evhttp_clear_headers(&headers);
	return  xret;
}

int http_get_request_uri(struct evhttp_request *evh_req, struct http_request *request)
{
	int xret=-1;
	struct evkeyvalq headers;

	xret = http_get_headers(evh_req, &headers);
	if(xret != 0)
	{
		return xret;
	}

	const char *keyring_uuid_str=NULL, *is_valid=NULL, *sni=NULL;

	keyring_uuid_str = evhttp_find_header(&headers, "keyring_id");
	if(keyring_uuid_str == NULL)
	{
		xret =-1;
		goto finish;
	}
    request->keyring_uuid_str = strdup(keyring_uuid_str);

    is_valid = evhttp_find_header(&headers, "is_valid");
	if(is_valid == NULL)
	{
		xret =-1;
		goto finish;
	}
    request->is_valid = atoi(is_valid);

    sni = evhttp_find_header(&headers, "sni");
    if (sni == NULL)
    {
		xret =-1;
		goto finish;
	}
	request->sni = strdup(sni);
	xret=0;
finish:
	evhttp_clear_headers(&headers);
	mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "[Thread %d]Received request for uri, kering_id:%s, sni:%s, valid:%d", request->thread_id, request->keyring_uuid_str, request->sni, request->is_valid);
	return xret;
}

static void evhttp_socket_close_cb(struct evhttp_connection *evcon, void __attribute__((__unused__))*arg)
{
    if (NULL == evcon)
	{
        goto finish;
    }

finish:
    return;
}

static int x509_get_rkey(X509 *origin, char *keyring_uuid_str, char *rkey, int is_valid)
{
    unsigned int len = 0, i = 0;
    char hex[EVP_MAX_MD_SIZE] = {0};
    unsigned char fdig[EVP_MAX_MD_SIZE] = {0};

    X509_digest(origin, EVP_sha1(), fdig, &len);
    for (i = 0; i < len ; ++i){
        sprintf(hex + i * sizeof(unsigned char) * 2, "%02x", fdig[i]);
    }
    struct pxy_obj_keyring *pxy_obj = get_obj_for_id(keyring_uuid_str);
    if (pxy_obj != NULL)
    {
        snprintf(rkey, DATALEN, "%s:%lu:%s:%d", keyring_uuid_str, pxy_obj->op_time, hex, is_valid);
        goto finish;
    }
    snprintf(rkey, DATALEN, "%s:%s:%d", keyring_uuid_str, hex, is_valid);
finish:
	if (pxy_obj)
        keyring_table_free(pxy_obj);
    return 0;
}

static int redis_sync_command(struct http_request *request,  struct redisContext __attribute__((__unused__))*c)
{
	int xret = -1;
	redisReply *reply;

    x509_forge_thread *thread_ctx = threads + request->thread_id;

	reply = (redisReply *)redisCommand(thread_ctx->sync, "GET %s", request->rkey);
	if (NULL == reply)
		goto free;

	switch (readBytes(reply->str)) {
		case '+' :
			mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Sends the certificate information to the requestor");
		 	xret = redis_clnt_send(request, reply);
            break;
		case '$' :
			mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Generating certificate information");
            xret = redis_clnt_pdu_send(request);
			goto finish;
		default  :
			mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Read redis data(%s) return code failed", request->rkey);
			evhttp_send_error(request->evh_req, HTTP_NOTFOUND, 0);
			goto free;
	}
	xret = 0;

free:
	freeReplyObject(reply);

finish:
	return xret;
}

static int get_x509_msg(struct http_request *request, char *input, ssize_t inputlen)
{
	request->origin = x509_get_ca_from_msg(input, inputlen + 1);
    if (request->origin == NULL){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "The certificate is invalid.");
		request_destroy(request);
        return -1;
    }
    x509_get_rkey(request->origin, request->keyring_uuid_str, request->rkey, request->is_valid);
    if (request->rkey[0] == '\0'){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Get the redis key from the certificate failed");
        return -1;
    }
    mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Redis key is %s", request->rkey);

	return 0;
}

int get_keyring_from_local( struct http_request *request)
{
	int ret =0;
	ret = redis_clnt_pdu_send(request);
	if (ret < 0)
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Local sign certificate failed");
	}
	return ret;
}

int get_keyring_form_redis(x509_forge_thread *info, struct http_request *request, int mode)
{
	int ret=0;

	switch (mode)
	{
		case 0:
			ret = redis_sync_command(request, info->sync);
			if (ret < 0)
			{
				mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to get information from redis server");
			}
			break;
		case 1:
			ret = redisAsyncCommand(info->cl_ctx, redis_get_callback, request, "GET %s", request->rkey);
			if (ret < 0)
			{
				mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to get information from redis server");
			}
			break;
		default:
			break;
	}
	return ret;
}

static int get_keypair_cache(x509_forge_thread *info, struct http_request *request, int mode)
{
	return info->sync == NULL ? get_keyring_from_local(request) : get_keyring_form_redis(info, request, mode);
}

void http_get_cb(struct evhttp_request *evh_req, void *arg)
{
    int xret = -1;
	struct http_request *request = NULL;
	struct evbuffer * evbuf_body = NULL;
	char *input = NULL; ssize_t inputlen=0;
	x509_forge_thread *info = (x509_forge_thread *)arg;

	if (evhttp_request_get_command(evh_req) != EVHTTP_REQ_POST)
	{
		mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "FAILED (post type)");
		goto error;
	}
	evhttp_connection_set_closecb(evhttp_request_get_connection(evh_req), evhttp_socket_close_cb, NULL);

	xret = http_get_heal_check(evh_req);
	if(xret != 0)
	{
		evhttp_send_reply(evh_req, 200, "OK", NULL);
		return;
	}

	request = (struct http_request *) kmalloc (sizeof(struct http_request), MPF_CLR, -1);
	request->thread_id  = info->id;
	request->evh_req    = evh_req;
	clock_gettime(CLOCK_MONOTONIC,&request->create_time);

	xret = http_get_request_uri(evh_req, request);
	if(xret != 0)
	{
		goto error;
	}
	evbuf_body = evhttp_request_get_input_buffer(evh_req);
	if (!evbuf_body || 0==(inputlen = evbuffer_get_length(evbuf_body)) || !(input = (char *)evbuffer_pullup(evbuf_body,inputlen)))
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to get certificate information.");
		goto error;
	}

	xret = get_x509_msg(request, input, inputlen);
	if (xret != 0)
	{
		goto error;
	}
	FS_operate(g_FP_instance.handle, g_FP_instance.line_ids[KEYPAIR_ACTION_REQ], 0, FS_OP_ADD, 1);

	xret = get_keypair_cache(info, request, g_cert_store->mode);
	if (xret >= 0)
	{
		goto finish;
	}
error:
	request_destroy(request);
	evhttp_socket_send_error(evh_req, HTTP_BADREQUEST);
finish:
    return;
}

int redis_sync_connect(struct redisContext **c)
{
	struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    *c = redisConnectWithTimeout(g_cert_store->local_redis_ip, g_cert_store->local_redis_port, timeout);
    if (*c == NULL || (*c)->err)
	{
        if (*c) {
            mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Sync connection error: %s", (*c)->errstr);
            redisFree(*c);
            *c = NULL;
        } else {
            mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Connection error: can't allocate g_cert_store context");
        }
        return -1;;
    }
    return 0;
}

int local_redis_connect(struct event_base *base, x509_forge_thread *thread)
{
	int ret = -1;
	ret = redis_sync_connect(&thread->sync);
	if (ret < 0 || !thread->sync)
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Initialize the sync redis connection is failure");
	}

	if(g_cert_store->mode==0)
	{
		return ret;
	}
	ret = redis_async_connect(base, &thread->cl_ctx);
	if (ret < 0 || !thread->cl_ctx)
	{
	   mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Initialize the redis connection is failure");
	}
	return ret;
}

int load_default_ca(x509_forge_thread *thread)
{
	int ret=0;

	if(g_cert_store->local_debug == 0)
	{
		return 0;
	}
    ret = x509_key_pair_init(g_cert_store->ca_path, &thread->def.key, &thread->def.root);
    if (ret < 0 || !(thread->def.key) || !(thread->def.root))
	{
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to initialize the x509 certificate");
        goto finish;
    }
    ret = x509_key_pair_init(g_cert_store->uninsec_path, &thread->def.insec_key, &thread->def.insec_root);
    if (ret < 0 || !(thread->def.key) || !(thread->def.root))
	{
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to initialize the insec x509 certificate");
        goto finish;
    }
finish:
	return ret;
}

static void *keyring_work_thread_run(void *arg)
{
    int xret                          = -1;
	struct evhttp_bound_socket *bound = NULL;
    x509_forge_thread *thread_ctx = (x509_forge_thread *)arg;

    struct event_base *base = event_base_new();
    if (! base) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Can'thread_ctx allocate event base");
        return NULL;
    }
    struct evhttp *http = evhttp_new(base);
	if (!http) {
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "couldn'thread_ctx create evhttp. Exiting.");
		goto error;
	}
    thread_ctx->base = base;
	local_redis_connect(base, thread_ctx);

	xret = load_default_ca(thread_ctx);
    if (xret < 0)
	{
        goto error;
    }
    evhttp_set_cb(http, "/ca", http_get_cb, thread_ctx);

    bound = evhttp_accept_socket_with_handle(http, thread_ctx->accept_fd);
    if (bound != NULL) {
        mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Bound(%p) to port %d - Awaiting connections ... ", bound, g_cert_store->listen_port);
	}
	mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Work thread %u is run...", thread_ctx->id);

	event_base_dispatch(base);
error:
    event_base_free(base);
    return NULL;
}

#ifdef SOCK_NONBLOCK
#define EVUTIL_SOCK_NONBLOCK SOCK_NONBLOCK
#else
#define EVUTIL_SOCK_NONBLOCK 0x4000000
#endif
#ifdef SOCK_CLOEXEC
#define EVUTIL_SOCK_CLOEXEC SOCK_CLOEXEC
#else
#define EVUTIL_SOCK_CLOEXEC 0x80000000
#endif
#ifdef EFD_NONBLOCK
#define EVUTIL_EFD_NONBLOCK EFD_NONBLOCK
#else
#define EVUTIL_EFD_NONBLOCK 0x4000
#endif
#ifdef EFD_CLOEXEC
#define EVUTIL_EFD_CLOEXEC EFD_CLOEXEC
#else
#define EVUTIL_EFD_CLOEXEC 0x8000
#endif

static int
evutil_fast_socket_nonblocking(evutil_socket_t fd)
{
#ifdef _WIN32
	return evutil_make_socket_nonblocking(fd);
#else
	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
		return -1;
	}
	return 0;
#endif
}

static int
evutil_fast_socket_closeonexec(evutil_socket_t fd)
{
#if !defined(_WIN32) && defined(EVENT__HAVE_SETFD)
	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
		return -1;
	}
#endif
	return 0;
}

evutil_socket_t
evutil_socket_(int domain, int type, int protocol)
{
	evutil_socket_t r;
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
	r = socket(domain, type, protocol);
	if (r >= 0)
		return r;
	else if ((type & (SOCK_NONBLOCK|SOCK_CLOEXEC)) == 0)
		return -1;
#endif
#define SOCKET_TYPE_MASK (~(EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC))
	r = socket(domain, type & SOCKET_TYPE_MASK, protocol);
	if (r < 0)
		return -1;
	if (type & EVUTIL_SOCK_NONBLOCK) {
		if (evutil_fast_socket_nonblocking(r) < 0) {
			evutil_closesocket(r);
			return -1;
		}
	}
	if (type & EVUTIL_SOCK_CLOEXEC) {
		if (evutil_fast_socket_closeonexec(r) < 0) {
			evutil_closesocket(r);
			return -1;
		}
	}
	return r;
}

static evutil_socket_t
evhttp_listen_socket_byuser(const struct sockaddr *sa, int socklen,
                            unsigned flags, int backlog)
{
	evutil_socket_t fd;
	int on = 1;
	int family = sa ? sa->sa_family : AF_UNSPEC;
	int socktype = SOCK_STREAM | EVUTIL_SOCK_NONBLOCK;

	if (flags & LEV_OPT_CLOSE_ON_EXEC)
		socktype |= EVUTIL_SOCK_CLOEXEC;

	fd = evutil_socket_(family, socktype, 0);
	if (fd == -1)
		return fd;

	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0)
		goto err;
	if (flags & LEV_OPT_REUSEABLE) {
		if (evutil_make_listen_socket_reuseable(fd) < 0)
			goto err;
	}
	if (flags & LEV_OPT_REUSEABLE_PORT) {
		if (evutil_make_listen_socket_reuseable_port(fd) < 0){
			goto err;
        }
	}
	if (sa) {
		if (bind(fd, sa, socklen)<0)
			goto err;
	}
    if (listen(fd, backlog) == -1) {
        goto err;
    }
	return fd;
err:
	evutil_closesocket(fd);
	return fd;
}

void redis_link_detection(uint32_t uid, int argc, char **argv)
{
    int tid = 0, xret = 0;
    x509_forge_thread *info   = NULL;
    x509_forge_thread *threads = (x509_forge_thread *)argv;

    unsigned int thread_nu = g_cert_store->thread_nu;
    for (tid = 0; tid < (int)thread_nu; tid++)
	{
        info  = threads + tid;
        if(info->sync == NULL && info->base != NULL)
		{
			redisFree(info->sync);
            xret = redis_sync_connect(&info->sync);
            if (xret < 0 || !info->sync)
			{
                mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "[%d]trying to connect sync redis failed", tid);
                continue;
            }
			else
            {
                mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "[%d]trying to connect sync redis success", tid);
            }

			if(g_cert_store->mode == 0)
			{
				continue;
			}
			xret = redis_async_connect(info->base, &info->cl_ctx);
			if (xret < 0 || !info->cl_ctx)
			{
			    mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "[%d]trying to connect rsync redis failed", tid);
			}
			else
			{
			    mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "[%d]trying to connect rsync redis success", tid);
			}
        }
    }
}

static int keyring_server_init(const char *main_profile)
{
	int xret=-1;
    unsigned int tid          = 0;
    x509_forge_thread *thread   = NULL;
	uint32_t tm_link_detetion = 0;
    unsigned int thread_nu = g_cert_store->thread_nu;

    /* Create a new evhttp object to handle requests. */
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(struct sockaddr_in));
    sin.sin_family = AF_INET;
    sin.sin_port   = htons(g_cert_store->listen_port);
    evutil_socket_t accept_fd = evhttp_listen_socket_byuser((struct sockaddr*)&sin, sizeof(struct sockaddr_in), LEV_OPT_REUSEABLE_PORT|LEV_OPT_CLOSE_ON_FREE, -1);
    if (accept_fd < 0)
	{
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Could not create a listen!");
        goto finish;
    }
    threads = (x509_forge_thread *)calloc(thread_nu, sizeof(x509_forge_thread));
    if (! threads)
	{
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Can't allocate thread descriptors");
        goto finish;
    }
    memset(threads, 0, thread_nu * sizeof(x509_forge_thread));

    /* Create threads after we've done all the libevent setup. */
    for (tid = 0; tid < thread_nu; tid++)
	{
        thread            = threads + tid;
        thread->id        = tid;
        thread->accept_fd = accept_fd;
        thread->routine   = keyring_work_thread_run;

        if (pthread_create(&thread->pid, thread->attr, thread->routine, &threads[tid])){
            mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s", strerror(errno));
			goto finish;
		}
		if (pthread_detach(thread->pid)){
            mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s", strerror(errno));
			goto finish;
		}
    }
#ifdef RT_TMR_ADVANCED
    /*Create timers to monitor redis connections **/
    tm_link_detetion = tmr_create(1, "Redis link detection",
                                  redis_link_detection, 1, (char **)threads, 5);
    if (((int32_t)tm_link_detetion < 0)){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s",
                        "Can not create link-detection timer for redis\n");
    }
    tmr_start(tm_link_detetion);
#endif
    FOREVER{
        sleep(1);
    }
finish:
    return xret;
}

static void
rt_get_pname_by_pid(pid_t pid, char *task_name)
{
#define BUF_SIZE 1024
    char proc_pid_path[BUF_SIZE];
    char buf[BUF_SIZE];
    sprintf(proc_pid_path, "/proc/%d/status", pid);
    FILE* fp = fopen(proc_pid_path, "r");
    if(NULL != fp){
        if( fgets(buf, BUF_SIZE-1, fp)== NULL ){
            fclose(fp);
        }
        fclose(fp);
        sscanf(buf, "%*s %s", task_name);
    }
}

void sigproc(int __attribute__((__unused__))sig)
{
    int tid            = 0;
    x509_forge_thread *thread     = NULL;

    for (tid = 0; tid < g_cert_store->thread_nu; tid++)
	{
        thread = threads + tid;
        if (thread->sync)
		{
            redisAsyncDisconnect(thread->cl_ctx);
            redisFree(thread->sync);
        }
        event_base_free(thread->base);
    }
    kfree(threads);

	if(g_cert_store->instance)
	{
		maat_free(g_cert_store->instance);
	}
	kfree(g_cert_store);

    exit(1);
}

static int kerying_fs_stat_init(const char *main_profile)
{
	int value=0, i=0, prometheus_port =0;
	char stat_path[128] ={0}, pname[32]={0};
	char prometheus_url_path[1024] = {0};
    char statsd_server[32];	int statsd_port;

    MESA_load_profile_int_def(main_profile, "stat", "statsd_port", &(statsd_port), 8126);
	MESA_load_profile_string_def(main_profile, "stat", "statsd_server", statsd_server, sizeof(statsd_server), "");
	MESA_load_profile_int_def(main_profile, "stat", "statsd_set_prometheus_port", &(prometheus_port), 9001);
	MESA_load_profile_string_def(main_profile, "stat", "statsd_set_prometheus_url_path", prometheus_url_path, sizeof(prometheus_url_path), "/certstore_prometheus");

	g_FP_instance.favorite=FS_CALC_CURRENT;
	strcpy(g_FP_instance.histogram_bins, FP_HISTOGRAM_BINS);

	FS_library_set_prometheus_port(prometheus_port);
	FS_library_set_prometheus_url_path(prometheus_url_path);
	FS_library_init();

	screen_stat_handle_t fs=NULL;
	fs=FS_create_handle();
	rt_get_pname_by_pid(getpid(), &pname[0]);
	FS_set_para(fs, APP_NAME, pname, strlen(pname)+1);
	value=1;
	FS_set_para(fs, OUTPUT_PROMETHEUS, &value, sizeof(value));
	value=0;
	FS_set_para(fs, FLUSH_BY_DATE, &value, sizeof(value));
	snprintf(stat_path, 128, "%s/fs2_%s.status", "./logs", pname);
	FS_set_para(fs, OUTPUT_DEVICE, stat_path, strlen(stat_path)+1);
	value=1;
	FS_set_para(fs, PRINT_MODE, &value, sizeof(value));
	value=1;
	FS_set_para(fs, CREATE_THREAD, &value, sizeof(value));
	value=2;
	FS_set_para(fs, STAT_CYCLE, &value, sizeof(value));
	if(strlen(statsd_server)>0 && statsd_port!=0)
	{
		FS_set_para(fs, STATS_SERVER_IP, statsd_server, strlen(statsd_server)+1);
		FS_set_para(fs, STATS_SERVER_PORT, &(statsd_port), sizeof(statsd_port));
	}
	FS_set_para(fs, HISTOGRAM_GLOBAL_BINS, g_FP_instance.histogram_bins, strlen(g_FP_instance.histogram_bins)+1);

	const char* __str_stat_spec_map[KEYPAIR_ACTION_MAX]={0};
	__str_stat_spec_map[KEYPAIR_ACTION_REQ]="ask_kyr_req";
	__str_stat_spec_map[KEYPAIR_ACTION_SQL]="rd_cache";
	__str_stat_spec_map[KEYPAIR_ACTION_SIGN]="x509_sign";
	__str_stat_spec_map[KEYPAIR_ACTION_ERR]="ask_kyr_fail";

	for (i = 0; i < KEYPAIR_ACTION_MAX; i++)
	{
		g_FP_instance.line_ids[i] = FS_register(fs, FS_STYLE_FIELD, FS_CALC_CURRENT, __str_stat_spec_map[i]);
	}
	FS_start(fs);
	g_FP_instance.handle = fs;

	for (i = 0; i <= KEYPAIR_ACTION_SIGN; i++)
	{
		int size = strlen(__str_stat_spec_map[i]) + strlen("(us)");
		char buff[size+1];
		snprintf(buff,sizeof(buff),"%s(us)",(char*)__str_stat_spec_map[i]);
		g_FP_instance.field_id[i]=FS_register_histogram(g_FP_instance.handle, g_FP_instance.favorite, buff,
										  1, 30*1000,3);
	}
    return 0;
}

char *keyring_get_value_string(cJSON *pxy_profile_keyring, const char *keyword)
{
	cJSON *item = cJSON_GetObjectItem(pxy_profile_keyring, keyword);
	if(item==NULL || item->type!=cJSON_String)
	{
		return NULL;
	}

	return item->valuestring;
}

int keyring_get_value_number(cJSON *pxy_profile_keyring, const char *keyword)
{
	cJSON *item = cJSON_GetObjectItem(pxy_profile_keyring, keyword);
	if(item==NULL && item->type!=cJSON_Number)
	{
		return -1;
	}

	return item->valueint;
}

void keyring_table_new_cb(const char *table_name, const char *key, const char* table_line, void **ad, long argl, void * argp)
{
    char *private_file=NULL, *public_file=NULL;

	cJSON* pxy_profile_keyring = cJSON_Parse(table_line);
	if(pxy_profile_keyring == NULL)
	{
		mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "DECRYPTION_KEYRING parse table_line failed %s", table_line);
		return;
	}

	struct pxy_obj_keyring *pxy_obj = (struct pxy_obj_keyring *)malloc(sizeof(struct pxy_obj_keyring));
    if (!pxy_obj)
    {
    	cJSON_Delete(pxy_profile_keyring);
        mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Can not alloc, %s", strerror(errno));
        return;
    }
    memset(pxy_obj, 0, sizeof(struct pxy_obj_keyring));
	atomic64_set(&pxy_obj->ref_cnt, 1);

	char *value_string = keyring_get_value_string(pxy_profile_keyring, "uuid");
	if(value_string != NULL)
	{
		memcpy(pxy_obj->keyring_uuid, value_string, strlen(value_string));
	}
	value_string = keyring_get_value_string(pxy_profile_keyring, "keyring_type");
	if(value_string != NULL)
	{
		memcpy(pxy_obj->keyring_type, value_string, strlen(value_string));
	}
	value_string = keyring_get_value_string(pxy_profile_keyring, "private_key_file");
	if(value_string != NULL)
	{
		private_file = value_string;
	}
	value_string = keyring_get_value_string(pxy_profile_keyring, "public_key_file");
	if(value_string != NULL)
	{
		public_file = value_string;
	}
	value_string = keyring_get_value_string(pxy_profile_keyring, "public_key_algo");
	if(value_string != NULL)
	{
		memcpy(pxy_obj->public_algo, value_string, strlen(value_string));
	}
	value_string = keyring_get_value_string(pxy_profile_keyring, "crl");
	if(value_string != NULL)
	{
		memcpy(pxy_obj->v3_ctl, value_string, strlen(value_string));
	}

	int value_int = keyring_get_value_number(pxy_profile_keyring, "reissue_expiry_date");
	if(value_int > 0)
	{
		pxy_obj->expire_time = value_int;
	}

	value_int = keyring_get_value_number(pxy_profile_keyring, "is_send_root_cert");
	if(value_int > 0)
	{
		pxy_obj->is_send = value_int;
	}

	value_int = keyring_get_value_number(pxy_profile_keyring, "use_hsm");
	if(value_int > 0)
	{
		pxy_obj->use_hsm = value_int;
	}

	value_int = keyring_get_value_number(pxy_profile_keyring, "slot_id");
	if(value_int > 0)
	{
		pxy_obj->slot_id = value_int;
	}
	pxy_obj->op_time = time(NULL);

    /*Load PUBLICKEY***/
    if ((pxy_obj->issuer = x509_get_root_ca(public_file, pxy_obj->is_send, pxy_obj->keyring_type, &pxy_obj->stack_ca)) == NULL ){
        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "initialize the x509 publickey failed, the keyring id is %s",
                         pxy_obj->keyring_uuid);
		goto finish;
	}

	if(pxy_obj->use_hsm == 0)
	{
		/*Load PRIVATEKEY**/
	    if ((pxy_obj->key = cert_load_key(private_file)) == NULL){
	        mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "initialize the x509 privatekey failed, the keyring id is %s",
	                         pxy_obj->keyring_uuid);
	        goto finish;
	    }
	}

    mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "initialize the x509 certificate, the keyring id is %s",
                     pxy_obj->keyring_uuid);
	*ad = pxy_obj;
finish:
	cJSON_Delete(pxy_profile_keyring);
	pxy_profile_keyring=NULL;
	return;
}

void keyring_table_dup_cb(const char *table_name, void **to, void **from, long argl, void *argp)
{
	struct pxy_obj_keyring* pxy_obj=(struct pxy_obj_keyring*)(*from);
    if(pxy_obj==NULL)
    {
        *to=NULL;
        return;
    }
	atomic64_inc (&pxy_obj->ref_cnt);
    *((struct pxy_obj_keyring**)to)=pxy_obj;
}

#define MAAT_INPUT_JSON			1
#define MAAT_INPUT_REDIS    	2
#define MAAT_INPUT_FILE        	0

struct maat *create_maat_feather(const char * main_profile)
{
	struct maat *target=NULL;
	int input_mode = 0,log_level=0;
	int ret = 0, effect_interval = 60;
	int redis_db_idx = 0, deferred_load_on = 0;
	char table_info[128]={0}, json_cfg_file[128] = {0};
	char redis_server[128]={0},redis_port_range[128]={0};
	int redis_port_begin=0, redis_port_end=0;
	int redis_port_select=0;

	MESA_load_profile_int_def(main_profile,    "MAAT", "maat_json_switch", &(input_mode), 0);
	MESA_load_profile_string_def(main_profile, "MAAT", "table_info", table_info, sizeof(table_info), "");
    MESA_load_profile_int_def(main_profile,    "MAAT", "effective_interval", &(effect_interval), 60);
	MESA_load_profile_string_def(main_profile, "MAAT", "pxy_obj_keyring", json_cfg_file, sizeof(json_cfg_file), "");
    MESA_load_profile_int_def(main_profile,    "MAAT", "deferred_load_on", &(deferred_load_on), 0);
	MESA_load_profile_int_def(main_profile,    "MAAT", "log_level", &(log_level), LOG_LEVEL_FATAL);

	MESA_load_profile_string_def(main_profile, "MAAT_REDIS", "ip", redis_server, sizeof(redis_server), "");
    MESA_load_profile_string_def(main_profile, "MAAT_REDIS", "port", redis_port_range, sizeof(redis_server), "6379");
    MESA_load_profile_int_def(main_profile,    "MAAT_REDIS", "dbindex", &(redis_db_idx), 0);

	ret=sscanf(redis_port_range,"%d-%d", &redis_port_begin, &redis_port_end);
	if(ret==1)
	{
		redis_port_select=redis_port_begin;
	}
	else if(ret==2)
	{
		srand(time(NULL));
		redis_port_select=redis_port_begin+rand()%(redis_port_end-redis_port_begin);
	}
	else
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Invalid redis port range %s, MAAT init failed.", redis_port_range);
	}

	effect_interval *= 1000;//convert s to ms
	assert(strlen(redis_server)!=0 || strlen(json_cfg_file)!=0);

    struct maat_options *opts = maat_options_new();
	maat_options_set_instance_name(opts, "certstore");
	maat_options_set_caller_thread_number(opts, g_cert_store->thread_nu);
	maat_options_set_logger(opts, "logs/maat.log", (enum log_level)log_level);
	switch (input_mode)
	{
		case MAAT_INPUT_JSON:
			maat_options_set_json_file(opts, json_cfg_file);
			break;
		case MAAT_INPUT_REDIS:
			maat_options_set_redis(opts, redis_server, redis_port_select, redis_db_idx);
			break;
		default: mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Invalid MAAT Input Mode: %d.", input_mode);
			goto error_out;
			break;
	}

	if (deferred_load_on)
	{
		maat_options_set_deferred_load_on(opts);
	}
	maat_options_set_foreign_cont_dir(opts, "./foreign_files/");

	target = maat_new(opts, table_info);
	if (!target)
	{
		mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s MAAT init failed.", __FUNCTION__);
		goto error_out;
	}
    maat_options_free(opts);
	return target;

error_out:
	maat_options_free(opts);
	return NULL;
}

int kerying_profile_init(const char * main_profile)
{
	int ret=0;

	g_cert_store->instance = create_maat_feather(main_profile);
	if(!g_cert_store->instance)
	{
		goto finish;
	}
	ret=maat_plugin_table_ex_schema_register(g_cert_store->instance, "DECRYPTION_KEYRING", keyring_table_new_cb,keyring_table_free_cb,
 						             			  keyring_table_dup_cb, 0, NULL);
	if(ret<0)
	{
		goto finish;
	}
	return 0;
finish:
	mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "certstore register table DECRYPTION_KEYRING failed");
	exit(1);
	return -1;
}

int cert_store_session_init(const char *main_profile)
{
    kerying_fs_stat_init(main_profile);

    kerying_profile_init(main_profile);

    keyring_server_init(main_profile);

    return 0;
}

