/*
 * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
 *
 * PROPRIETARY/CONFIDENTIAL
 *
 * This software is the confidential and proprietary information of Samsung
 * Electronics Co., Ltd. ("Confidential Information"). You shall not disclose such
 * Confidential Information and shall use it only in accordance with the terms of
 * the license agreement you entered into with Samsung Electronics Co., Ltd. ("SAMSUNG")
 * SAMSUNG MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SAMSUNG SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

#include <stdint.h>

#include "dsmsta.h"
#define LOG_TAG "[DSMSTA]"
#include "dsmsta_log.h"
#include "qsee_core.h"
#include "qsee_env.h"
#include "qsee_fs.h"
#include "qsee_sfs.h"

#include "ICredentials.h"
#include "CDsms.h"
#include "CDsms_open.h"
#include "object.h"

#define MAX_TA_NAME_LEN 32

static const char *ta_allowlist[] = {
#ifdef __DEBUG__
	"dsms_sample_client",
#endif
};

static int32_t dsms_storage_read(char *buffer)
{
	int fd, read_bytes;
	int32_t ret;
	sfs_file_entry *file_list;
	uint32_t file_list_len;

	LOGD("dsms_storage_read");

	if (qsee_sfs_get_file_list(&file_list, &file_list_len) != 0) {
		LOGE("Error occurred getting file list");
		return DSMS_RECV_FAILURE;
	}

	if (file_list_len == 0) {
		LOGD("File list is empty");
		return DSMS_RECV_FAILURE;
	}

	fd = qsee_sfs_open(file_list[0].file_name, O_RDONLY);
	if (!fd) {
		LOGE("Error occurred while opening file");
		ret = DSMS_RECV_FAILURE;
		goto out;
	}

	read_bytes = qsee_sfs_read(fd, buffer, DSMS_MAX_BUFFER);
	if (read_bytes < 0) {
		LOGE("Error occurred while reading file");
		ret = DSMS_RECV_FAILURE;
		goto out;
	}

	if (qsee_sfs_close(fd) != 0) {
		LOGE("Error occurred while closing file");
		ret = DSMS_RECV_FAILURE;
		goto out;
	}

	if (qsee_sfs_rm(file_list[0].file_name) != 0) {
		LOGE("Error occurred while removing file");
		ret = DSMS_RECV_FAILURE;
		goto out;
	}

	if (read_bytes < DSMS_MAX_BUFFER) {
		buffer[read_bytes] = '\0';
	} else {
		buffer[DSMS_MAX_BUFFER - 1] = '\0';
	}
	ret = DSMS_RECV_SUCCESS;

	LOGD("Read from SFS file %s: '%s'", file_list[0].file_name, buffer);

out:
	qsee_sfs_clean_file_list(file_list);

	return ret;
}

static int32_t get_app_name(Object cred, char *app_name,
			    size_t app_name_buf_sz, size_t *app_name_sz)
{
	return ICredentials_getValueByName(cred, "n", 1, app_name,
					   app_name_buf_sz, app_name_sz);
}

static int32_t check_permission(char *caller_ta_name,
				size_t caller_ta_name_size)
{
	uint32_t i, allowlist_len;

	allowlist_len = sizeof(ta_allowlist) / sizeof(*ta_allowlist);
	for (i = 0; i < allowlist_len; i++) {
		if (strncmp(ta_allowlist[i], caller_ta_name,
				caller_ta_name_size) == 0) {
			return Object_OK;
		}
	}

	return Object_ERROR_INVALID;
}

int32_t tz_module_open(uint32_t uid, Object cred, Object *objOut)
{
	int32_t ret;
	char caller_ta_name[MAX_TA_NAME_LEN] = {0};
	size_t caller_ta_name_size = 0;

	LOGD("tz_module_open");

	ret = get_app_name(cred, caller_ta_name, sizeof(caller_ta_name),
			   &caller_ta_name_size);
	if (!Object_isOK(ret)) {
		LOGE("Error occurred while getting caller name");
		return ret;
	}

	LOGD("Caller TA name: %s", caller_ta_name);

	ret = check_permission(caller_ta_name, caller_ta_name_size);
	if (!Object_isOK(ret)) {
		LOGE("Permission denied to %s", caller_ta_name);
		return ret;
	}

	switch (uid) {
		case CDsms_UID:
			return CDsms_open(cred, objOut);
		default:
			break;
	}

	*objOut = Object_NULL;

	return Object_ERROR_INVALID;
}

void tz_app_init(void)
{
	/* Get the current log mask */
	uint8_t log_mask = qsee_log_get_mask();

	/* Enable debug level logs */
	qsee_log_set_mask(log_mask | QSEE_LOG_MSG_DEBUG);

	LOGD("tz_app_init");
}

void tz_app_shutdown(void)
{
	LOGD("tz_app_shutdown");
}

void tz_app_cmd_handler(void *cmd_ptr, uint32_t cmdlen,
			void *rsp_ptr, uint32_t rsplen)
{
	int ret;
	dsms_req_t *req = (dsms_req_t *)cmd_ptr;
	dsms_rsp_t *rsp = (dsms_rsp_t *)rsp_ptr;

	LOGD("tz_app_cmd_handler");

	if (qsee_is_ns_range(cmd_ptr, cmdlen)) {
		LOGE("cmd_ptr data is not in secure memory");
		ret = DSMS_RECV_FAILURE;
		goto out;
	}

	if (qsee_is_ns_range(rsp_ptr, rsplen)) {
		LOGE("rsp_ptr data is not in secure memory");
		ret = DSMS_RECV_FAILURE;
		goto out;
	}

	switch (req->cmd_id) {
	case DSMS_CMD_RECV:
		LOGD("DSMS_CMD_RECV");
		ret = dsms_storage_read(rsp->buffer);
		break;
	default:
		LOGE("Unknown command id");
		ret = DSMS_RECV_FAILURE;
	}

out:
	rsp->ret = ret;
	return;
}
