#include <string.h>
#include "Log.h"
#include "File.h"
#include "Tlv.h"
#include "EtcCmdHandler.h"
#include <sys/stat.h>

namespace vendor   {
namespace samsung  {
namespace hardware {
namespace security {
namespace drk      {

#define INT_SIZE    4
int32_t EtcCmdHandler::fileCommand(int32_t cmd, Bytes& req, Bytes& resp)
{
    int32_t   ret = NOT_ERROR;
    File      lcFile;
    FILEBLOB  lcFB;
    char      fname [MAX_FILE_PATH_LEN] = {0};
    char      *pName = NULL;

    ret = decodeMessage(cmd, req, (void *)&lcFB);
    if (ret != NOT_ERROR) {
        LOGME("decodeMessage ret : %d", ret);
        goto end;
    }

    if(cmd == CMD_VND_APPENDFILE) {
        snprintf(fname, MAX_FILE_PATH_LEN - 1, "%s/%s", DRK_DATA_DIR, (char *)lcFB.name);
        pName = fname;
    } else {
        pName = (char *)lcFB.name;
    }

    if((ret = checkFileStatus(pName, lcFB.data.length())) != NOT_ERROR) {
        LOGME("Invalid fileStatus ret : %d", ret);
        goto end;
    }

    switch (cmd) {
        case CMD_VND_READFILE :
        {
            ret = lcFile.openFile((char *)lcFB.name, READ);
            if (ret != NOT_ERROR) {
                LOGME("open file error");
                ret = ERR_FILE_NOT_OPEN;
                goto end;
            }

            ret = lcFile.readFile(lcFB.data);
            if (ret != NOT_ERROR) {
                LOGME("read file error");
                ret = ERR_FILE_READ;
                goto end;
            }
        }
        break;
        case CMD_VND_SAVEFILE :
        {
            ret = lcFile.openFile((char *)lcFB.name, WRITE);
            if (ret != NOT_ERROR) {
                LOGME("open file error");
                ret = ERR_FILE_NOT_OPEN;
                goto end;
            }

            ret = lcFile.writeFile(lcFB.data);
            if (ret != NOT_ERROR) {
                LOGME("write file error");
                ret = ERR_FILE_WRITE;
                goto end;
            }
        }
        break;
        case CMD_VND_APPENDFILE :
        {
            ret = lcFile.openFile((char *)fname, APPEND);
            if (ret != NOT_ERROR) {
                LOGME("open file error");
                ret = ERR_FILE_NOT_OPEN;
                goto end;
            }

            ret = lcFile.writeFile(lcFB.data);
            if (ret != NOT_ERROR) {
                LOGME("write file error");
                ret = ERR_FILE_WRITE;
                goto end;
            }
        }
        break;
        case CMD_VND_FILESIZE :
        {
            uint8_t   lc_filesize_buf[INT_SIZE] = {0};

            ret = lcFile.openFile((char *)lcFB.name, READ);
            if (ret != NOT_ERROR) {
                LOGME("open file error");
                ret = ERR_FILE_NOT_OPEN;
                goto end;
            }

            if (lcFile.getFileSize() == 0) {
                LOGME("get file size error");
                ret = ERR_FILE_NOT_OPEN;
                goto end;
            }
            SET_UINT32(lc_filesize_buf, 0, lcFile.getFileSize());
            lcFB.data.set(lc_filesize_buf, INT_SIZE);
        }
        break;
        default :
            ret = ERR_NOT_IMPLEMENTED;
            goto end;
    }

    ret = encodeMessage(cmd, (void *)&lcFB, resp);
    if (ret != NOT_ERROR) {
        LOGME("encodeMessage ret : %d", ret);
    }

end:
    return ret;
}

int32_t EtcCmdHandler::encodeMessage(int32_t cmd, void *param, Bytes& out)
{
    int32_t ret = NOT_ERROR, skip_tlv = 0;
    TLV lc_tlv;
    lc_tlv.setMode(TLV_START_EX);
    switch (cmd) {
        case CMD_VND_READFILE: {
            FILEBLOB *p1 = (FILEBLOB *)param;

            lc_tlv.add(TLV_RFDATA, (uint8_t *)p1->data, p1->data.length());

            ret = NOT_ERROR;
        }
        break;
        case CMD_VND_FILESIZE: {
            FILEBLOB  *p1 = (FILEBLOB *)param;

            lc_tlv.add(TLV_RFSIZE, (uint8_t *)p1->data, p1->data.length());

            ret = NOT_ERROR;
        }
        break;
        case CMD_VND_SAVEFILE:
        case CMD_VND_APPENDFILE:
            skip_tlv = 1;
            ret = NOT_ERROR;
        break;
        default:
            LOGME("encode_params does not support this %X command", cmd);
            ret = ERR_UNSUPPORTED_CMD;
            break;
    }

    if (ret == NOT_ERROR && skip_tlv == 0) {
        lc_tlv.encode(out);
    }
    return ret;
}

int32_t EtcCmdHandler::checkFileStatus(char *filePath, uint32_t blobSize) {
    const char tokenTable[][MAX_FILE_PATH_LEN] = {
        {"../"},
        {"dev_root.dat"},
    };
    const size_t tableSize = sizeof(tokenTable)/sizeof(tokenTable[0]);
    size_t i = 0;
    struct stat fileStat;

    if(filePath == NULL || blobSize > MAX_LOG_FILE_LEN) {
        return ERR_INVALID_ARGUMENT;
    }

    for(; i < tableSize; i++){
        if(strstr(filePath, tokenTable[i]) != NULL)
            return ERR_WRONG_ORDER;
    }

    if(stat(filePath, &fileStat) == 0) {
        if(fileStat.st_size >= MAX_LOG_FILE_LEN - blobSize) {
            return ERR_BUFFER_OVERFLOW;
        }
    }

    return NOT_ERROR;
}

int32_t EtcCmdHandler::decodeMessage(int32_t cmd, Bytes& in, void *param)
{
    int32_t ret = NOT_ERROR;
    TLV   lc_tlv;

    ret = lc_tlv.decode(in);
    if (ret != NOT_ERROR) {
        return ret;
    }

    switch (cmd) {
        case CMD_VND_SAVEFILE:
        case CMD_VND_APPENDFILE:
        {
            FILEBLOB  *p1 = (FILEBLOB *)param;

            ret = lc_tlv.get(TLV_WFNAME, p1->name);
            if (ret != NOT_ERROR) {
                LOGME("Getting file path was failed : %d", ret);
                goto end;
            }

            ret = lc_tlv.get(TLV_WFDATA, p1->data);
            if (ret != NOT_ERROR) {
                LOGME("Getting file data was failed : %d", ret);
                goto end;
            }
            ret = NOT_ERROR;
        }
        break;
        case CMD_VND_READFILE:
        case CMD_VND_FILESIZE: {
            FILEBLOB  *p1 = (FILEBLOB *)param;

            ret = lc_tlv.get(TLV_RFNAME, p1->name);

            if (ret != NOT_ERROR) {
                LOGME("Getting file path was failed : %d", ret);
                goto end;
            }
            ret = NOT_ERROR;
        }
        break;
        default:
            LOGME("encode_params does not support this %X command", cmd);
            ret = ERR_UNSUPPORTED_CMD;
            break;
    }
end:
    return ret;
}

int32_t EtcCmdHandler::CmdHandler(int32_t cmd, uint32_t cpid, Bytes& req, Bytes& resp)
{
    int32_t ret = NOT_ERROR;
    PRINT_FUNC_START(cmd);
    if ((ret = mlockGuard.Enter(cmd, cpid)) != NOT_ERROR) {
        LOGMD("To try to get lock in CmdHandler but returned with : %d", ret);
        PRINT_FUNC_END(cmd, ret);
        return ret;
    }
    switch (cmd) {
        /* file command */
        case CMD_VND_READFILE :
        case CMD_VND_SAVEFILE :
        case CMD_VND_FILESIZE :
        case CMD_VND_APPENDFILE :
            ret = fileCommand(cmd, req, resp);
            break;
        /* hermes command */
        case CMD_VND_CHECK_PDP_DATA_KEYSTORE:// check sp app loading
            ret = CheckAppLoaded();
            break;
        case CMD_VND_GET_PDP_DATA: // bigdata json format
            ret = GetBigdataMsg(resp);
            break;
        case CMD_VND_GET_PDP_INFO: // get securehw info
            ret = GetInfo(resp);
            break;
        case CMD_VND_SET_PDP_DATA: // Provisioning (QC:ARI enable // LSI:key provisioning)
            ret = KeyProvision();
            break;
        case CMD_VND_CHECK_PDP_DATA: // verify provisioning (QC : health data check // LSI : verify key provisioning)
            ret = VerifyKeyprovision();
            break;
        case CMD_VND_PDP_SELFTEST: // selftest
            ret = Selftest(resp);
            break;
        case CMD_VND_TERMINATE_PDP: // terminate sercurehw
            ret = Terminate();
            break;
        case CMD_VND_UPDATE_CRYPTO_FW: // update Crypto FW
            ret = UpdateCryptoFW(resp);
            break;
        case CMD_VND_SAVE_BIGDATA: // save bigdata
            ret = SaveBigData(req);
            break;
        case CMD_VND_SECNVM_POWER_ON: // Turn on SecNVM
            ret = SecnvmPowerOn();
            break;
        case CMD_VND_SECNVM_POWER_OFF: // Turn off SecNVM
            ret = SecnvmPowerOff();
            break;

        default :
            LOGME("Not supported command : %X", cmd);
            ret = ERR_COMMON_NOT_SUPPORT_CMD;
            break;
    }
    mlockGuard.Leave(cmd, ret);
    PRINT_FUNC_END(cmd, ret);
    return ret;
}



}  // namespace drk
}  // namespace security
}  // namespace hardware
}  // namespace samsung
}  // namespace vendor
