/*

 * Copyright (c) 2024 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

#include <pthread.h>

#include <stdlib.h>



#ifndef APPSPAWN_CLIENT

#include "appspawn_sandbox.h"

#endif

#include "appspawn_client.h"

#include "appspawn_mount_permission.h"

#include "appspawn_msg.h"

#include "appspawn_permission.h"

#include "appspawn_utils.h"

#include "json_utils.h"

#include "securec.h"

#ifdef APPSPAWN_ENABLE_SPM

#include "appspawn_hook.h"

#include "appspawn_manager.h"

#endif



typedef struct TagParseJsonContext {

    SandboxQueue permissionQueue;

    int32_t maxPermissionIndex;

    uint32_t inited;

    AppSpawnClientType type;

} ParseJsonContext, PermissionManager;



static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;

static PermissionManager g_permissionMgr[CLIENT_MAX] = {};



#ifdef APPSPAWN_SANDBOX_NEW

static int ParseAppSandboxConfig(const cJSON *root, PermissionManager *mgr)

{

    // conditional

    cJSON *json = cJSON_GetObjectItemCaseSensitive(root, "conditional");

    APPSPAWN_CHECK(json != NULL, return 0, "No found conditional in config");



    // permission

    cJSON *config = cJSON_GetObjectItemCaseSensitive(json, "permission");

    APPSPAWN_CHECK(config != NULL && cJSON_IsArray(config), return 0, "No found permission in config");



    uint32_t configSize = cJSON_GetArraySize(config);

    for (uint32_t i = 0; i < configSize; i++) {

        json = cJSON_GetArrayItem(config, i);

        char *name = GetStringFromJsonObj(json, "name");

        APPSPAWN_CHECK(name != NULL, break, "No found name in config");



        int ret = AddSandboxPermissionNode(name, &mgr->permissionQueue);

        APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);

    }

    return 0;

}



static PermissionManager *GetPermissionMgrByType(AppSpawnClientType type)

{

    APPSPAWN_CHECK_ONLY_EXPER(type < CLIENT_MAX, return NULL);

    g_permissionMgr[type].type = type;

    return &g_permissionMgr[type];

}

#else



static int ParsePermissionConfig(const cJSON *permissionConfigs, PermissionManager *mgr)

{

    cJSON *config = NULL;

    cJSON_ArrayForEach(config, permissionConfigs)

    {

        const char *name = config->string;

        int ret = AddSandboxPermissionNode(name, &mgr->permissionQueue);

        APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);

    }

    return 0;

}



static int ParseAppSandboxConfig(const cJSON *appSandboxConfig, PermissionManager *mgr)

{

    cJSON *configs = cJSON_GetObjectItemCaseSensitive(appSandboxConfig, "permission");

    APPSPAWN_CHECK(configs != NULL && cJSON_IsArray(configs), return 0, "No permission in json");



    int ret = 0;

    uint32_t configSize = (uint32_t)cJSON_GetArraySize(configs);

    for (uint32_t i = 0; i < configSize; i++) {

        cJSON *json = cJSON_GetArrayItem(configs, i);

        ret = ParsePermissionConfig(json, mgr);

        APPSPAWN_CHECK(ret == 0, return ret, "Parse permission config fail result: %{public}d ", ret);

    }

    return ret;

}



static PermissionManager *GetPermissionMgrByType(AppSpawnClientType type)

{

    APPSPAWN_CHECK_ONLY_EXPER(type < CLIENT_MAX, return NULL);

    g_permissionMgr[0].type = CLIENT_FOR_APPSPAWN;

    return &g_permissionMgr[0];

}

#endif



static int LoadPermissionConfig(PermissionManager *mgr)

{

    (void)ParseJsonConfig("etc/sandbox",

                          mgr->type == CLIENT_FOR_APPSPAWN ? APP_SANDBOX_FILE_NAME : RENDER_SANDBOX_FILE_NAME,

                          ParseAppSandboxConfig, mgr);

    size_t count = sizeof(g_spawnerPermissionList) / sizeof(g_spawnerPermissionList[0]);

    for (size_t i = 0; i < count; i++) {

        AddSandboxPermissionNode(g_spawnerPermissionList[i], &mgr->permissionQueue);

    }



    mgr->maxPermissionIndex = PermissionRenumber(&mgr->permissionQueue);

    return 0;

}



static inline int32_t CheckPermissionManager(PermissionManager *mgr)

{

    if (mgr != NULL && mgr->inited) {

        return 1;

    }

    return 0;

}



static int32_t PMGetPermissionIndex(AppSpawnClientType type, const char *permission)

{

    PermissionManager *mgr = GetPermissionMgrByType(type);

    APPSPAWN_CHECK_ONLY_EXPER(CheckPermissionManager(mgr), return INVALID_PERMISSION_INDEX);

    return GetPermissionIndexInQueue((SandboxQueue *)&mgr->permissionQueue, permission);

}



static int32_t PMGetMaxPermissionIndex(AppSpawnClientType type)

{

    PermissionManager *mgr = GetPermissionMgrByType(type);

    APPSPAWN_CHECK_ONLY_EXPER(CheckPermissionManager(mgr), return INVALID_PERMISSION_INDEX);

    return mgr->maxPermissionIndex;

}



static const char *PMGetPermissionByIndex(AppSpawnClientType type, int32_t index)

{

    PermissionManager *mgr = GetPermissionMgrByType(type);

    APPSPAWN_CHECK_ONLY_EXPER(CheckPermissionManager(mgr), return NULL);

    if (mgr->maxPermissionIndex <= index) {

        return NULL;

    }

    const SandboxPermissionNode *node = GetPermissionNodeInQueueByIndex((SandboxQueue *)&mgr->permissionQueue, index);

    return PERMISSION_NAME(node);

}



APPSPAWN_STATIC int LoadPermission(AppSpawnClientType type)

{

    APPSPAWN_LOGW("LoadPermission %{public}d", type);

    pthread_mutex_lock(&g_mutex);

    PermissionManager *mgr = GetPermissionMgrByType(type);

    if (mgr == NULL) {

        pthread_mutex_unlock(&g_mutex);

        return APPSPAWN_ARG_INVALID;

    }



    if (mgr->inited) {

        pthread_mutex_unlock(&g_mutex);

        return 0;

    }

    mgr->maxPermissionIndex = -1;

    mgr->permissionQueue.type = 0;

    OH_ListInit(&mgr->permissionQueue.front);

    int ret = LoadPermissionConfig(mgr);

    if (ret == 0) {

        mgr->inited = 1;

    }

    pthread_mutex_unlock(&g_mutex);

    return ret;

}



APPSPAWN_STATIC void DeletePermission(AppSpawnClientType type)

{

    pthread_mutex_lock(&g_mutex);

    PermissionManager *mgr = GetPermissionMgrByType(type);

    if (mgr == NULL || !mgr->inited) {

        pthread_mutex_unlock(&g_mutex);

        return;

    }

    DeleteSandboxPermissions(&mgr->permissionQueue);

    mgr->maxPermissionIndex = -1;

    mgr->inited = 0;

    pthread_mutex_unlock(&g_mutex);

}



int32_t GetPermissionMaxCount()

{

    int32_t maxCount = 0;

    for (uint32_t i = 0; i < CLIENT_MAX; i++) {

        int32_t max = PMGetMaxPermissionIndex(i);

        if (max > maxCount) {

            maxCount = max;

        }

    }

    return maxCount;

}



int32_t GetPermissionIndex(AppSpawnClientHandle handle, const char *permission)

{

    APPSPAWN_CHECK(permission != NULL, return INVALID_PERMISSION_INDEX, "Invalid permission name");

    if (handle == NULL) {

        return PMGetPermissionIndex(CLIENT_FOR_APPSPAWN, permission);

    }

    AppSpawnReqMsgMgr *reqMgr = (AppSpawnReqMsgMgr *)handle;

    return PMGetPermissionIndex(reqMgr->type, permission);

}



int32_t GetMaxPermissionIndex(AppSpawnClientHandle handle)

{

    if (handle == NULL) {

        return PMGetMaxPermissionIndex(CLIENT_FOR_APPSPAWN);

    }

    AppSpawnReqMsgMgr *reqMgr = (AppSpawnReqMsgMgr *)handle;

    return PMGetMaxPermissionIndex(reqMgr->type);

}



const char *GetPermissionByIndex(AppSpawnClientHandle handle, int32_t index)

{

    if (handle == NULL) {

        return PMGetPermissionByIndex(CLIENT_FOR_APPSPAWN, index);

    }

    AppSpawnReqMsgMgr *reqMgr = (AppSpawnReqMsgMgr *)handle;

    return PMGetPermissionByIndex(reqMgr->type, index);

}



#ifdef APPSPAWN_ENABLE_SPM

static int InitPermissionQueueForSPM(AppSpawnMgr *mgr)

{

    APPSPAWN_CHECK_ONLY_EXPER(mgr != NULL, return -1);



    // Get permission manager for appspawn

    PermissionManager *permMgr = GetPermissionMgrByType(CLIENT_FOR_APPSPAWN);

    APPSPAWN_CHECK_ONLY_EXPER(permMgr != NULL, return -1);

    APPSPAWN_CHECK_ONLY_EXPER(CheckPermissionManager(permMgr), return -1);



    // Expose permission queue to AppSpawnContent for SPM

    mgr->content.permissionQueue = &permMgr->permissionQueue;

    return 0;

}

#endif



__attribute__((constructor)) static void LoadPermissionModule(void)

{

    (void)LoadPermission(CLIENT_FOR_APPSPAWN);

    (void)LoadPermission(CLIENT_FOR_NWEBSPAWN);

#ifdef APPSPAWN_ENABLE_SPM

    (void)AddServerStageHook(STAGE_SERVER_PRELOAD, HOOK_PRIO_COMMON, InitPermissionQueueForSPM);

#endif

}



__attribute__((destructor)) static void DeletePermissionModule(void)

{

    DeletePermission(CLIENT_FOR_APPSPAWN);

    DeletePermission(CLIENT_FOR_NWEBSPAWN);

}