#include "em_common.h"

static uint8_t *mPrefixBody;
static uint32_t mPrefixBodyLen;

static uint8_t *mCert;
static uint32_t mCertLen;

static uint8_t *mEtokenB64;
static uint32_t mEtokenB64Len;

static uint8_t *mErsdB64;
static uint32_t mErsdB64Len;

static uint8_t *mRxIVB64;
static uint32_t mRxIVB64Len;

static int em_ess_parse_command_v1(int type, uint8_t *cmd);

static uint8_t *em_ess_get_prefixbody(uint32_t *len)
{
	if (len)
		*len = mPrefixBodyLen;

	return mPrefixBody;
}

static uint8_t *em_ess_get_cert(uint32_t *len)
{
	if (len)
		*len = mCertLen;

	return mCert;
}

static uint8_t *em_ess_get_etokenb64(uint32_t *len)
{
	if (len)
		*len = mEtokenB64Len;

	return mEtokenB64;
}

static uint8_t *em_ess_get_rxivb64(uint32_t *len)
{
	if (len)
		*len = mRxIVB64Len;

	return mRxIVB64;
}

static uint8_t *em_ess_get_ersdb64(uint32_t *len)
{
	if (len)
		*len = mErsdB64Len;

	return mErsdB64;
}

static void em_ess_clear_value(void)
{
	if (mPrefixBody) {
		em_free(mPrefixBody);
		mPrefixBody = NULL;
		mPrefixBodyLen = 0;
	}

	if (mCert) {
		em_free(mCert);
		mCert = NULL;
		mCertLen = 0;
	}

	if (mEtokenB64) {
		em_free(mEtokenB64);
		mEtokenB64 = NULL;
		mEtokenB64Len = 0;
	}

	if (mErsdB64) {
		em_free(mErsdB64);
		mErsdB64 = NULL;
		mErsdB64Len = 0;
	}

	if (mRxIVB64) {
		em_free(mRxIVB64);
		mRxIVB64 = NULL;
		mRxIVB64Len = 0;
	}
}

static int em_ess_init(int type, uint8_t *cmd)
{
	int ret;
	int version = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_INIT, cmd);

	em_ess_clear_value();

	if (strlen((const char *)cmd) < 3) {
		LOGE("cmd length isn't normal(%s)\n", cmd);
		ret = EM_ERR_EM_ESS_INIT_CMD;
		goto out;
	}

	if (memcmp(cmd, EM_MAGIC_ESS_VERSION, strlen(EM_MAGIC_ESS_VERSION)) == 0)
		version = 1;

	switch (version) {
	case 1:
		ret = em_ess_parse_command_v1(type, cmd);
		break;
	default:
		LOGE("It's not supported version(%d)\n", version);
		ret = EM_ERR_EM_ESS_INIT_UNKNOWN_VERSION;
		goto out;
	}

out:
	return ret;
}

static int em_ess_parse_command_v1(int type, uint8_t *org_cmd)
{
	int ret;

	uint32_t i = 0;
	uint32_t size = 0;
	int paramCount = 1;
	int maxParams = 0;
	int constOfParam[EM_LEN_ESS_V1_MAX_PARAMS][2] = { {0, } };

	uint8_t NeedPrefixBody = 0;
	uint8_t NeedCert = 0;
	uint8_t NeedRxIV = 0;
	uint8_t NeedEToken = 0;
	uint8_t NeedERSD = 0;

	char *cmd = NULL;
	char *ptr = NULL;
	char *params[EM_LEN_ESS_V1_MAX_PARAMS + 1] = {0, };
	char *sBodyMsg = NULL;

	uint8_t server_hash[EM_LEN_SHA256] = {0,};
	uint8_t hash[EM_LEN_SHA256] = {0,};

#ifdef ESS_DEBUG
	char tmp[4000] = {0,};
	int tmp_size = 0;
	int offset = 0;
#endif

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_PARSE_COMMAND_V1, org_cmd);

	cmd = (char *)em_calloc(strlen((char *)org_cmd) + 1, 1);
	if (cmd == NULL) {
		LOGE("Failed to alloc cmd memory\n");
		ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_CMD_ALLOC;
		goto out;
	}

	size = strlen((char *)org_cmd);
	for (i = 0; i < size; i++) {
		if (org_cmd[i] == '\r' || org_cmd[i] == '\n')
			org_cmd[i] = 0;
	}

	size = strlen((char *)org_cmd);
	memcpy(cmd, org_cmd, size);
	cmd[size] = '\0';

#ifdef ESS_DEBUG
	tmp_size = size / 500;
	for (i = 0; i < tmp_size; i++) {
		memcpy(tmp, org_cmd + (500 * i), 500);
		tmp[500] = 0;
		LOGE("cmd[%d] = %s\n", i, tmp);
	}

	memcpy(tmp, org_cmd + (500 * tmp_size), size - (500 * tmp_size) + 1);
	tmp[size - (500 * tmp_size) + 2] = 0;
	LOGE("cmd[%d] : %s\n", tmp_size, tmp);
#endif

	switch (type) {
	case EM_TYPE_ESS_MAKE_TOKEN:
		maxParams = EM_LEN_ESS_V1_TRM_NUM_PARAMS;
		constOfParam[1][0] = EM_LEN_ESS_V1_MAX_ESS;
		constOfParam[2][0] = EM_LEN_ESS_V1_MAX_NUM_MODES;
		constOfParam[3][0] = EM_LEN_ESS_V1_MAX_MODES_SET;
		constOfParam[4][0] = EM_LEN_ESS_V1_MAX_DATE;
		constOfParam[4][1] = EM_LEN_ESS_V1_NUM_CHECK;
		constOfParam[5][0] = EM_LEN_ESS_V1_MAX_DATE;
		constOfParam[5][1] = EM_LEN_ESS_V1_NUM_CHECK;
		constOfParam[6][0] = EM_LEN_ESS_V1_MAX_SINGLE_ID;
		constOfParam[7][0] = EM_LEN_ESS_V1_MAX_OTP;
		constOfParam[8][0] = EM_LEN_ESS_V1_MAX_CERT_LEN;
		constOfParam[9][0] = EM_LEN_ESS_V1_MAX_CERT;
		constOfParam[10][0] = EM_LEN_ESS_V1_MAX_HASH;
		NeedPrefixBody = 1;
		NeedCert = 1;
		break;
	case EM_TYPE_ESS_DELETE_TOKEN:
		maxParams = EM_LEN_ESS_V1_TDM_MAX_PARAMS;
		constOfParam[1][0] = EM_LEN_ESS_V1_MAX_ESS;
		constOfParam[2][0] = EM_LEN_ESS_V1_MAX_CERT_LEN;
		constOfParam[3][0] = EM_LEN_ESS_V1_MAX_CERT;
		constOfParam[4][0] = EM_LEN_ESS_V1_MAX_HASH;
		NeedPrefixBody = 1;
		NeedCert = 1;
		break;
	case EM_TYPE_ESS_INSTALL_TOK:
		maxParams = EM_LEN_ESS_V1_INST_MAX_PARAMS;
		constOfParam[1][0] = EM_LEN_ESS_V1_MAX_IV_LEN;
		constOfParam[1][1] = EM_LEN_ESS_V1_NUM_CHECK;
		constOfParam[2][0] = EM_LEN_ESS_V1_MAX_IV;
		constOfParam[3][0] = EM_LEN_ESS_V1_MAX_ETOKEN_LEN;
		constOfParam[3][1] = EM_LEN_ESS_V1_NUM_CHECK;
		constOfParam[4][0] = EM_LEN_ESS_V1_MAX_ETOKEN;
		constOfParam[5][0] = EM_LEN_ESS_V1_MAX_HASH;
		NeedRxIV = 1;
		NeedEToken = 1;
		break;
	case EM_TYPE_ESS_MAKE_RECOVERY_MSG:
		maxParams = EM_LEN_ESS_V1_RM_NUM_PARAMS;
		constOfParam[1][0] = EM_LEN_ESS_V1_MAX_ESS;
		constOfParam[2][0] = EM_LEN_ESS_V1_MAX_SINGLE_ID;
		constOfParam[3][0] = EM_LEN_ESS_V1_MAX_OTP;
		constOfParam[4][0] = EM_LEN_ESS_V1_MAX_CERT_LEN;
		constOfParam[5][0] = EM_LEN_ESS_V1_MAX_CERT;
		constOfParam[6][0] = EM_LEN_ESS_V1_MAX_HASH;
		NeedPrefixBody = 1;
		NeedCert = 1;
		break;
	case EM_TYPE_ESS_RECOVERY_DATA:
		maxParams = EM_LEN_ESS_V1_RECOVERY_MAX_PARAMS;
		constOfParam[1][0] = EM_LEN_ESS_V1_MAX_IV_LEN;
		constOfParam[1][1] = EM_LEN_ESS_V1_NUM_CHECK;
		constOfParam[2][0] = EM_LEN_ESS_V1_MAX_IV;
		constOfParam[3][0] = EM_LEN_ESS_V1_MAX_ERSD_LEN;
		constOfParam[3][1] = EM_LEN_ESS_V1_NUM_CHECK;
		constOfParam[4][0] = EM_LEN_ESS_V1_MAX_ERSD;
		constOfParam[5][0] = EM_LEN_ESS_V1_MAX_HASH;
		NeedRxIV = 1;
		NeedERSD = 1;
		break;

	default:
		LOGE("It's not supported type");
		ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_UNKNOWN_CMD;
		goto out;
	}

	size = strlen((const char *)cmd);
	for (i = 0; i < size; i++) {
		if (cmd[i] == ':')
			paramCount++;
	}

	if (paramCount < maxParams) {
		LOGE("Improper parameters(type:%08x,%d)\n", type, paramCount);
		ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_IMPROPER_PRM;
		goto out;
	}

	ptr = em_strtok_with_null((char *)cmd, ":");
	i = 0;
	while (ptr != NULL) {
		if (*ptr)
			size = strlen(ptr);
		else
			size = 0;

		LOGD("Param[%d] = %s\n", i, ptr);
		if (i == 0)
			goto next;

		if ((int)size > constOfParam[i][0]) {
			LOGE("Improper size of parameter#%d(%d, %d)\n", i, size, constOfParam[i][0]);
			ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_PARAM_SIZE;
			goto out;
		}

		if (constOfParam[i][1] == EM_LEN_ESS_V1_NUM_CHECK) {
			if (em_is_string_int(ptr) != EM_SUCCESS) {
				LOGE("Improper type of parameter#%d(%s)\n", i, ptr);
				ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_PARAM_TYPE;
				goto out;
			}
		}

next:
		params[i] = (char *)em_calloc(size+1, 1);
		if (*ptr)
			memcpy(params[i], ptr, size);

		i++;
		ptr = em_strtok_with_null(NULL, ":");
	}

	size = strlen((const char *)org_cmd) - strlen((const char *)params[maxParams-1]) - 1;
	sBodyMsg = (char *)em_calloc(size + 1, 1);
	if (sBodyMsg == NULL) {
		LOGE("Failed to alloc sBodymsg(%d)\n", size);
		ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_SBODY_ALLOC;
		goto out;
	}

	memcpy(sBodyMsg, org_cmd, size);

#ifdef ESS_DEBUG
	tmp_size = size / 500;
	for (i = 0; i < tmp_size; i++) {
		memcpy(tmp, sBodyMsg + (500*i), 500);
		tmp[500] = 0;
		LOGE("sBodyMsg[%d] = %s\n", i, tmp);
	}

	memcpy(tmp, sBodyMsg + (500 * tmp_size), size - (500 * tmp_size) + 1);
	tmp[size - (500 * tmp_size) + 2] = 0;
	LOGD("sBodyMsg[%d] : %s\n", tmp_size, tmp);
#endif

	em_hex_to_byte(params[maxParams-1], strlen(params[maxParams-1]), server_hash);

	ret = em_crypto_sha256((unsigned char *)sBodyMsg, (unsigned int)size, (unsigned char *)hash);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to make hash(%d)\n", ret);
		goto out;
	}

#ifdef ESS_DEBUG
	offset = 0;
	for (i = 0; i < 32; i++) {
		em_snprintf((char *)tmp + offset, 2 + 1, "%02x", hash[i]);
		offset += 2;
	}
	tmp[64] = '\0';
	LOGE("hash %s\n", tmp);

	offset = 0;
	for (i = 0; i < 32; i++) {
		em_snprintf((char *)tmp + offset, 2 + 1, "%02x", server_hash[i]);
		offset += 2;
	}
	tmp[64] = '\0';
	LOGE("server_hash %s\n", tmp);
#endif

	if (em_strncasecmp((const char *)hash, (const char *)server_hash, (size_t)EM_LEN_SHA256) != EM_SUCCESS) {
		LOGE("Failed to check hash\n");
		ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_HASH;
		goto out;
	}

	if (NeedCert) {
		size = strlen(params[maxParams-2])/2;
		mCert = (uint8_t *)em_calloc(size, 1);
		if (mCert == NULL) {
			LOGE("Failed to alloc memory for mCert\n");
			ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_CERT_ALLOC;
			goto out;
		}
		em_hex_to_byte(params[maxParams - 2], size * 2, mCert);
		if ((int)size != em_atoi(params[maxParams - 3])) {
			LOGE("Invalid length of certificate(%d/%d)\n", size, em_atoi(params[maxParams - 3]));
			ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_CERT_LEN;
			goto out;
		}
		mCertLen = size;
	}

	if (NeedPrefixBody) {
		size = (uint32_t)(strlen((char *)org_cmd) - strlen(params[maxParams - 1]) - strlen(params[maxParams - 2]) -
		       strlen(params[maxParams - 3]) - 4 - 3);
		mPrefixBody = (uint8_t *)em_calloc(size + 1, 1);
		if (mPrefixBody == NULL) {
			LOGE("Failed to alloc memory for mPrefixBody\n");
			ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_PREFIX_ALLOC;
			goto out;
		}

		memcpy(mPrefixBody, org_cmd + 3, size);
		mPrefixBodyLen = size;
	}

	if (NeedRxIV) {
		size = (uint32_t)strlen(params[2]);
		mRxIVB64 = (uint8_t *)em_calloc(size + 1, 1);
		if (mRxIVB64 == NULL) {
			LOGE("Failed to alloc memory for rxivb64\n");
			ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_RXIV_ALLOC;
			goto out;
		}
		memcpy(mRxIVB64, params[2], size);
		mRxIVB64Len = size;
	}

	if (NeedEToken) {
		size = (uint32_t)strlen(params[4]);
		mEtokenB64 = (uint8_t *)em_calloc(size + 1, 1);
		if (mEtokenB64 == NULL) {
			LOGE("Failed to alloc memory for mEtokenB64\n");
			ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_ETOKEN_ALLOC;
			goto out;
		}
		memcpy(mEtokenB64, params[4], size);
		mEtokenB64Len = size;
	}

	if (NeedERSD) {
		size = (uint32_t)strlen(params[4]);
		mErsdB64 = (uint8_t *)em_calloc(size + 1, 1);
		if (mErsdB64 == NULL) {
			LOGE("Failed to alloc memory for mErsdB64\n");
			ret = EM_ERR_EM_ESS_PARSE_COMMAND_V1_ERSD_ALLOC;
			goto out;
		}
		memcpy(mErsdB64, params[4], size);
		mErsdB64Len = size;
	}

	ret = EM_SUCCESS;
out:
	for (i = 0; i <= EM_LEN_ESS_V1_MAX_PARAMS; i++)
		if (params[i])
			em_free(params[i]);

	if (cmd)
		em_free(cmd);

	if (sBodyMsg)
		em_free(sBodyMsg);

	return ret;
}

int em_ess_command(em_context *ctx)
{
	int ret = 0;

	uint8_t *prefix_body = NULL;
	uint32_t len_prefix_body = 0;

	uint8_t *cert = NULL;
	uint32_t len_cert = 0;

	uint8_t *rxivb64 = NULL;
	uint32_t len_rxivb64 = 0;

	uint8_t *etokenb64 = NULL;
	uint32_t len_etoken_b64 = 0;

	uint8_t *recovery_b64 = NULL;
	uint32_t len_recovery_b64 = 0;

	uint8_t token_remove_flag = 0;
	uint8_t error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

	uint8_t *temp_str = NULL;
	uint32_t len_temp = 0;

	char *unknown_model = (char *)"UNKNOWN";
	char *p_model = NULL;

	uint32_t len_message_local_var;	//for exynos bootloader

	uint8_t *command = ctx->message;

	temp_str = (uint8_t *)em_calloc(EM_LEN_TOKEN_MODE_INFORMATION, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_COMMAND, ctx, temp_str);

	/*
	if (memcmp(command, EM_MAGIC_ESS_PREFIX_COMMAND, strlen(EM_MAGIC_ESS_PREFIX_COMMAND)) != 0) {
		LOGE("This command isn't our command\n");
		ret = EM_ERR_EM_ESS_COMMAND_UNKNOWN_COMMAND_PREFIX;
		goto out;
	}

	command += strlen(EM_MAGIC_ESS_PREFIX_COMMAND);
	*/

	if (ctx->flags[0] & EM_FLAGS_0_EXIST_MODEL_NAME ||
	    em_is_all_zero(ctx->model_name, EM_LEN_MODEL_NAME) != EM_SUCCESS)
		p_model = (char *)ctx->model_name;
	else
		p_model = (char *)unknown_model;

	memcpy(ctx->ess_command_type, command, 1);
	if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_DELETE_OFF, strlen(EM_MAGIC_ESS_PREFIX_HEADER_DELETE_OFF)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		token_remove_flag = 1;

		ret = em_ess_delete_token_offline(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to delete token offline(0x%08x)\n", ret);
			goto out;
		}
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_GET_INFO, strlen(EM_MAGIC_ESS_PREFIX_HEADER_GET_INFO)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		ret = em_ess_get_info(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get info(0x%08x)\n", ret);
			goto out;
		}
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_REQ_TOK, strlen(EM_MAGIC_ESS_PREFIX_HEADER_REQ_TOK)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		ret = em_ess_init(EM_TYPE_ESS_MAKE_TOKEN,
				  (uint8_t *)command + (uint8_t)strlen(EM_MAGIC_ESS_PREFIX_HEADER_REQ_TOK));
		if (ret != EM_SUCCESS) {
			LOGE("Failed to init ess for at command(%d)\n", ret);
			goto out;
		}

		prefix_body = em_ess_get_prefixbody(&len_prefix_body);
		if (prefix_body == NULL) {
			LOGE("Failed to get prefix_body\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_PREFIX_1;
			goto out;
		}
		memcpy(ctx->ess_prefixbody, prefix_body, len_prefix_body);
		ctx->flags[0] |= EM_FLAGS_0_EXIST_ESS_PREFIXBODY;

		cert = em_ess_get_cert(&len_cert);
		if (cert == NULL) {
			LOGE("Failed to get cert\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_CERT_1;
			goto out;
		}
		memcpy(ctx->cert, cert, len_cert);
		ctx->len_cert = len_cert;
		ctx->flags[0] |= EM_FLAGS_0_EXIST_CERT;

		if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_IMEI)) {
			ctx->flags[0] |= EM_FLAGS_0_EXIST_IMEI;
			memcpy(ctx->imei, (unsigned char *)"ffffffffffffff0", EM_LEN_IMEI);
		}

		if (strlen(p_model) > EM_LEN_MODEL_NAME) {
			LOGE("model_name isn't normal(%s/%u)\n", p_model, (uint32_t)strlen(p_model));
			ret = EM_ERR_EM_ESS_COMMAND_MODEL_NAME;
			goto out;
		}

		memcpy(ctx->model_name, p_model, strlen(p_model));
		ctx->flags[0] |= EM_FLAGS_0_EXIST_MODEL_NAME;

		ret = em_ess_make_token_request(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed make request token req msg(0x%08x)\n", ret);
			goto out;
		}
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_INSTALL_TOK,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_INSTALL_TOK)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_KEEPING_ITEM)) {
			LOGE("keeping item doesn't exist\n");
			ret = EM_ERR_EM_ESS_COMMAND_KEEPING_ITEM;
			goto out;
		}

		ret = em_ess_init(EM_TYPE_ESS_INSTALL_TOK,
				  (uint8_t *)command + (uint8_t)strlen(EM_MAGIC_ESS_PREFIX_HEADER_INSTALL_TOK));
		if (ret != EM_SUCCESS) {
			LOGE("Failed to init ess for install command(%d)\n", ret);
			goto out;
		}

		rxivb64 = em_ess_get_rxivb64(&len_rxivb64);
		if (rxivb64 == NULL) {
			LOGE("Failed to get rxivb64\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_RXIV_1;
			goto out;
		}
		memcpy(ctx->ess_iv_b64, rxivb64, len_rxivb64);
		ctx->flags[0] |= EM_FLAGS_0_EXIST_RXIV_B64;

		etokenb64 = em_ess_get_etokenb64(&len_etoken_b64);
		if (etokenb64 == NULL) {
			LOGE("Failed to get etokenb64\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_ETOKEN_1;
			goto out;
		}
		memcpy(ctx->ess_etoken_b64, etokenb64, len_etoken_b64);
		ctx->flags[0] |= EM_FLAGS_0_EXIST_ETOKEN_B64;

		ret = em_ess_install_token_v1(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to install ess token(0x%08x)\n", ret);
			goto out;
		}
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_DEL_TOK, strlen(EM_MAGIC_ESS_PREFIX_HEADER_DEL_TOK)) ==
		   0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		ret = em_ess_init(EM_TYPE_ESS_DELETE_TOKEN,
				  (uint8_t *)command + (uint8_t)strlen(EM_MAGIC_ESS_PREFIX_HEADER_DEL_TOK));
		if (ret != EM_SUCCESS) {
			LOGE("Failed to init ess for at command(%d)\n", ret);
			goto out;
		}

		token_remove_flag = 1;

		prefix_body = em_ess_get_prefixbody(&len_prefix_body);
		if (prefix_body == NULL) {
			LOGE("Failed to get prefix_body\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_PREFIX_2;
			goto out;
		}
		memcpy(ctx->ess_prefixbody, prefix_body, len_prefix_body);
		ctx->flags[0] |= EM_FLAGS_0_EXIST_ESS_PREFIXBODY;

		cert = em_ess_get_cert(&len_cert);
		if (cert == NULL) {
			LOGE("Failed to get cert\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_CERT_2;
			goto out;
		}
		memcpy(ctx->cert, cert, len_cert);
		ctx->len_cert = len_cert;
		ctx->flags[0] |= EM_FLAGS_0_EXIST_CERT;

		if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_IMEI)) {
			ctx->flags[0] |= EM_FLAGS_0_EXIST_IMEI;
			memcpy(ctx->imei, (unsigned char *)"ffffffffffffff0", EM_LEN_IMEI);
		}

		if (strlen(p_model) > EM_LEN_MODEL_NAME) {
			LOGE("model_name isn't normal(%s/%u)\n", p_model, (uint32_t)strlen(p_model));
			ret = EM_ERR_EM_ESS_COMMAND_MODEL_NAME_2;
			goto out;
		}

		memcpy(ctx->model_name, p_model, strlen(p_model));
		ctx->flags[0] |= EM_FLAGS_0_EXIST_MODEL_NAME;

		ret = em_ess_make_delete_request(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to kame delete req msg(0x%08x)\n", ret);
			goto out;
		}
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_REQ_RECOVERY,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_REQ_RECOVERY)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		ret = em_ess_init(EM_TYPE_ESS_MAKE_RECOVERY_MSG,
				  (uint8_t *)command + (uint8_t)strlen(EM_MAGIC_ESS_PREFIX_HEADER_REQ_RECOVERY));
		if (ret != EM_SUCCESS) {
			LOGE("Failed to init ess for at command(%d)\n", ret);
			goto out;
		}

		prefix_body = em_ess_get_prefixbody(&len_prefix_body);
		if (prefix_body == NULL) {
			LOGE("Failed to get prefix_body\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_PREFIX_1;
			goto out;
		}
		memcpy(ctx->ess_prefixbody, prefix_body, len_prefix_body);
		ctx->flags[0] |= EM_FLAGS_0_EXIST_ESS_PREFIXBODY;

		cert = em_ess_get_cert(&len_cert);
		if (cert == NULL) {
			LOGE("Failed to get cert\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_CERT_1;
			goto out;
		}
		memcpy(ctx->cert, cert, len_cert);
		ctx->len_cert = len_cert;
		ctx->flags[0] |= EM_FLAGS_0_EXIST_CERT;

		if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_IMEI)) {
			ctx->flags[0] |= EM_FLAGS_0_EXIST_IMEI;
			memcpy(ctx->keep.imei, (unsigned char *)"ffffffffffffff0", EM_LEN_IMEI);
		}

		if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_MODEL_NAME)) {
			if (strlen(p_model) > EM_LEN_MODEL_NAME) {
				LOGE("model_name isn't normal(%s/%u)\n", p_model, (uint32_t)strlen(p_model));
				ret = EM_ERR_EM_ESS_COMMAND_MODEL_NAME;
				goto out;
			}
			ctx->flags[0] |= EM_FLAGS_0_EXIST_MODEL_NAME;
			memcpy(ctx->model_name, p_model, strlen(p_model));
		}

		ret = em_ess_make_recovery_request(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to make recovery req msg(0x%08x)\n", ret);
			goto out;
		}
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_RECOVERY, strlen(EM_MAGIC_ESS_PREFIX_HEADER_RECOVERY)) ==
		   0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_KEEPING_ITEM)) {
			LOGE("keeping item doesn't exist\n");
			ret = EM_ERR_EM_ESS_COMMAND_KEEPING_ITEM;
			goto out;
		}

		ret = em_ess_init(EM_TYPE_ESS_RECOVERY_DATA,
				  (uint8_t *)command + (uint8_t)strlen(EM_MAGIC_ESS_PREFIX_HEADER_RECOVERY));
		if (ret != EM_SUCCESS) {
			LOGE("Failed to init ess for install command(%d)\n", ret);
			goto out;
		}

		rxivb64 = em_ess_get_rxivb64(&len_rxivb64);
		if (rxivb64 == NULL) {
			LOGE("Failed to get rxivb64\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_RXIV_1;
			goto out;
		}
		memcpy(ctx->ess_iv_b64, rxivb64, len_rxivb64);
		ctx->flags[0] |= EM_FLAGS_0_EXIST_RXIV_B64;

		recovery_b64 = em_ess_get_ersdb64(&len_recovery_b64);
		if (recovery_b64 == NULL) {
			LOGE("Failed to get recovery_b64\n");
			ret = EM_ERR_EM_ESS_COMMAND_GET_ERSD_B64;
			goto out;
		}
		memcpy(ctx->recovery_data, recovery_b64, len_recovery_b64);
		ctx->flags[0] |= EM_FLAGS_0_EXIST_ERSD_B64;

		ret = em_ess_recovery_esi_v1(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to ess recovery esi(0x%08x)\n", ret);
			goto out;
		}
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_HERE_I_AM,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_HERE_I_AM)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		ctx->len_message = 0;
		len_message_local_var = ctx->len_message;
		em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
				      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
				      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
		em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
				      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
		em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
				      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), NULL, EM_LEN_MESSAGE,
				      strlen(EM_MAGIC_ESS_AT_COMMAND_OK));
		ctx->message[len_message_local_var] = 0;
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
		ctx->len_message = len_message_local_var;
		goto out;
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_GET_MODES,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_GET_MODES)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NOK;

		if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_TOKEN))
			LOGE("token doesn't exist\n");

		ctx->len_message = 0;
		len_message_local_var = ctx->len_message;
		em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
				      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
				      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
		em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
				      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
		len_temp = 0;

		ret = em_token_get_only_mode(ctx, temp_str, &len_temp);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get mode information(0x%08x)\n", ret);
			em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_NOK,
					      strlen(EM_MAGIC_ESS_AT_COMMAND_NOK),
					      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE,
					      strlen(EM_MAGIC_ESS_AT_COMMAND_NOK));
			em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_GET_MODE_NO_TOKEN,
					      strlen(EM_MAGIC_GET_MODE_NO_TOKEN),
					      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE,
					      strlen(EM_MAGIC_GET_MODE_NO_TOKEN));
			em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_ERROR,
					      strlen(EM_MAGIC_ESS_AT_COMMAND_ERROR),
					      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE,
					      strlen(EM_MAGIC_ESS_AT_COMMAND_ERROR));
			em_snprintf((char *)(ctx->message + len_message_local_var), 8 + 1, "%08X", ret);
			len_message_local_var += 8;
		} else {
			em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
					      strlen(EM_MAGIC_ESS_AT_COMMAND_OK),
					      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE,
					      strlen(EM_MAGIC_ESS_AT_COMMAND_OK));
			em_ess_update_req_msg(ctx->message, &len_message_local_var,
					      (uint8_t *)EM_MAGIC_GET_MODE_FROM_TOKEN,
					      strlen(EM_MAGIC_GET_MODE_FROM_TOKEN),
					      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE,
					      strlen(EM_MAGIC_GET_MODE_FROM_TOKEN));
			em_ess_update_req_msg(ctx->message, &len_message_local_var, temp_str, len_temp, NULL,
					      EM_LEN_MESSAGE, EM_LEN_TOKEN_MODE_INFORMATION);
		}

		ctx->message[len_message_local_var] = 0;
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
		ctx->len_message = len_message_local_var;
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_SET_PRIORITY,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_SET_PRIORITY)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NOK;

		ret = em_token_set_priority_time(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to set priority_time(0x%08x)\n", ret);
			goto out;
		}

		ctx->message[ctx->len_message] = 0;
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
	} else if (memcmp(command, EM_MAGIC_ESS_PREFIX_HEADER_GET_PRIORITY,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_GET_PRIORITY)) == 0) {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NOK;

		ret = em_token_get_priority_time(ctx);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get priority_time(0x%08x)\n", ret);
			goto out;
		}

		ctx->message[ctx->len_message] = 0;
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
	} else {
		error_return_type = EM_RETURN_TYPE_ESS_ERROR_NG;

		LOGE("This isn't supported command(%s)\n", command);
		ret = EM_ERR_EM_ESS_COMMAND_UNKNOWN_COMMAND;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	if (ret != EM_SUCCESS) {
		ctx->len_message = 0;
		len_message_local_var = ctx->len_message;
		em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
				      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
				      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
		em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
				      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
		if (error_return_type == EM_RETURN_TYPE_ESS_ERROR_NOK) {
			em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_NOK,
						  strlen(EM_MAGIC_ESS_AT_COMMAND_NOK), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
						  EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_NOK));
		} else {
			em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_NG,
						  strlen(EM_MAGIC_ESS_AT_COMMAND_NG), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
						  EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_NG));
		}
		em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_ERROR,
				      strlen(EM_MAGIC_ESS_AT_COMMAND_ERROR), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
				      EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_ERROR));
		em_snprintf((char *)(ctx->message + len_message_local_var), 8 + 1, "%08X", ret);
		len_message_local_var += 8;
		ctx->message[len_message_local_var] = 0;

		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
		ctx->len_message = len_message_local_var;
		ret = EM_SUCCESS;
	}

	memcpy((char *)(ctx->message + ctx->len_message), EM_MAGIC_ESS_AT_COMMAND_END_LINE,
	       strlen(EM_MAGIC_ESS_AT_COMMAND_END_LINE));
	ctx->len_message += strlen(EM_MAGIC_ESS_AT_COMMAND_END_LINE);
	ctx->message[ctx->len_message] = 0;

	if (temp_str)
		em_free(temp_str);

	if (token_remove_flag == 1)
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE | EM_FLAGS_1_LTS_REMOVE_TOKEN;

	em_ess_clear_value();

	return ret;
}

int em_ess_get_command_type(em_context *ctx)
{
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_GET_COMMAND_TYPE, ctx);

	uint8_t *message = ctx->message;

	if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_DEL_TOK, strlen(EM_MAGIC_ESS_PREFIX_HEADER_DEL_TOK)) == 0) {
		ctx->cmd = EM_CMD_DELETE_TOKEN_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_REQ_TOK, strlen(EM_MAGIC_ESS_PREFIX_HEADER_REQ_TOK)) ==
		   0) {
		ctx->cmd = EM_CMD_REQ_TOKEN_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_INSTALL_TOK,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_INSTALL_TOK)) == 0) {
		ctx->cmd = EM_CMD_INSTALL_TOKEN_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_REQ_RECOVERY,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_REQ_RECOVERY)) == 0) {
		ctx->cmd = EM_CMD_REQ_RECOVERY_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_RECOVERY, strlen(EM_MAGIC_ESS_PREFIX_HEADER_RECOVERY)) ==
		   0) {
		ctx->cmd = EM_CMD_RECOVERY_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_GET_MODES,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_GET_MODES)) == 0) {
		ctx->cmd = EM_CMD_GET_MODES_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_HERE_I_AM,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_HERE_I_AM)) == 0) {
		ctx->cmd = EM_CMD_SUPPORT_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_GET_INFO, strlen(EM_MAGIC_ESS_PREFIX_HEADER_GET_INFO)) ==
		   0) {
		ctx->cmd = EM_CMD_GET_INFO_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_GET_PRIORITY,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_GET_PRIORITY)) == 0) {
		ctx->cmd = EM_CMD_GET_PRIORITY_TIME_ESS_V1;
	} else if (memcmp(message, EM_MAGIC_ESS_PREFIX_HEADER_SET_PRIORITY,
			  strlen(EM_MAGIC_ESS_PREFIX_HEADER_SET_PRIORITY)) == 0) {
		ctx->cmd = EM_CMD_SET_PRIORITY_TIME_ESS_V1;
	} else {
		LOGI("Unknown command(%02x/%02x/%02x/%02x)\n", message[0], message[1], message[2], message[3]);
	}

	ret = EM_SUCCESS;
out:
	return ret;
}
