2021-08-07 09:42:03 +01:00
|
|
|
/*
|
|
|
|
* nxdt_json.c
|
|
|
|
*
|
2024-04-12 10:47:36 +01:00
|
|
|
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
|
2021-08-07 09:42:03 +01:00
|
|
|
*
|
|
|
|
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
|
|
|
*
|
|
|
|
* nxdumptool is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* nxdumptool is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2024-04-30 22:01:42 +01:00
|
|
|
#include <core/nxdt_utils.h>
|
|
|
|
#include <core/nxdt_json.h>
|
2021-08-07 09:42:03 +01:00
|
|
|
|
|
|
|
#define JSON_GETTER(functype, vartype, jsontype, ...) \
|
|
|
|
vartype jsonGet##functype(const struct json_object *obj, const char *path) { \
|
|
|
|
vartype ret = (vartype)0; \
|
|
|
|
struct json_object *child = jsonGetObjectByPath(obj, path, NULL); \
|
|
|
|
if (child && jsonValidate##functype(child, ##__VA_ARGS__)) ret = (vartype)json_object_get_##jsontype(child); \
|
|
|
|
return ret; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define JSON_SETTER(functype, vartype, jsontype, ...) \
|
|
|
|
bool jsonSet##functype(const struct json_object *obj, const char *path, vartype value) { \
|
|
|
|
bool ret = false; \
|
|
|
|
struct json_object *child = jsonGetObjectByPath(obj, path, NULL); \
|
|
|
|
if (child && jsonValidate##functype(child, ##__VA_ARGS__)) { \
|
|
|
|
ret = (json_object_set_##jsontype(child, value) == 1); \
|
2022-07-12 17:34:49 +01:00
|
|
|
if (!ret) LOG_MSG_ERROR("Failed to update \"%s\"!", path); \
|
2021-08-07 09:42:03 +01:00
|
|
|
} \
|
|
|
|
return ret; \
|
|
|
|
}
|
|
|
|
|
|
|
|
struct json_object *jsonParseFromString(const char *str, size_t size)
|
|
|
|
{
|
|
|
|
if (!str || !*str)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Invalid parameters!");
|
2021-08-07 09:42:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Calculate string size if it wasn't provided. */
|
|
|
|
if (!size) size = (strlen(str) + 1);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
struct json_tokener *tok = NULL;
|
|
|
|
struct json_object *obj = NULL;
|
|
|
|
enum json_tokener_error jerr = json_tokener_success;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Allocate tokener. */
|
|
|
|
tok = json_tokener_new();
|
|
|
|
if (!tok)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("json_tokener_new failed!");
|
2021-08-07 09:42:03 +01:00
|
|
|
goto end;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Parse JSON buffer. */
|
|
|
|
obj = json_tokener_parse_ex(tok, str, (int)size);
|
|
|
|
if ((jerr = json_tokener_get_error(tok)) != json_tokener_success)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("json_tokener_parse_ex failed! Reason: \"%s\".", json_tokener_error_desc(jerr));
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
if (obj)
|
|
|
|
{
|
|
|
|
json_object_put(obj);
|
|
|
|
obj = NULL;
|
|
|
|
}
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
end:
|
|
|
|
if (tok) json_tokener_free(tok);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct json_object *jsonGetObjectByPath(const struct json_object *obj, const char *path, char **out_last_element)
|
|
|
|
{
|
|
|
|
const struct json_object *parent_obj = obj;
|
|
|
|
struct json_object *child_obj = NULL;
|
|
|
|
char *path_dup = NULL, *pch = NULL, *state = NULL, *prev_pch = NULL;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
if (!jsonValidateObject(obj) || !path || !*path)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Invalid parameters!");
|
2021-08-07 09:42:03 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Duplicate path to avoid problems with strtok_r(). */
|
|
|
|
if (!(path_dup = strdup(path)))
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Unable to duplicate input path! (\"%s\").", path);
|
2021-08-07 09:42:03 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Tokenize input path. */
|
|
|
|
pch = strtok_r(path_dup, "/", &state);
|
|
|
|
if (!pch)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Failed to tokenize input path! (\"%s\").", path);
|
2021-08-07 09:42:03 +01:00
|
|
|
goto end;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
while(pch)
|
|
|
|
{
|
|
|
|
prev_pch = pch;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
pch = strtok_r(NULL, "/", &state);
|
|
|
|
if (pch || !out_last_element)
|
|
|
|
{
|
|
|
|
/* Retrieve JSON object using the current path element. */
|
|
|
|
if (!json_object_object_get_ex(parent_obj, prev_pch, &child_obj))
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Failed to retrieve JSON object by key for \"%s\"! (\"%s\").", prev_pch, path);
|
2021-08-07 09:42:03 +01:00
|
|
|
break;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Update parent and child pointers if we can still proceed. */
|
|
|
|
if (pch)
|
|
|
|
{
|
|
|
|
parent_obj = child_obj;
|
|
|
|
child_obj = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No additional path elements can be found + the user wants the string for the last path element. */
|
|
|
|
/* Let's start by setting the last parent object as the return value. */
|
|
|
|
child_obj = (struct json_object*)parent_obj; /* Drop the const. */
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Duplicate last path element string. */
|
|
|
|
*out_last_element = strdup(prev_pch);
|
|
|
|
if (!*out_last_element)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Failed to duplicate last path element \"%s\"! (\"%s\").", prev_pch, path);
|
2021-08-07 09:42:03 +01:00
|
|
|
child_obj = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
end:
|
|
|
|
if (path_dup) free(path_dup);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
return child_obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
void jsonLogLastError(void)
|
|
|
|
{
|
|
|
|
size_t str_len = 0;
|
|
|
|
char *str = (char*)json_util_get_last_err(); /* Drop the const. */
|
|
|
|
if (!str || !(str_len = strlen(str))) return;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Remove line breaks. */
|
|
|
|
if (str[str_len - 1] == '\n') str[--str_len] = '\0';
|
|
|
|
if (str[str_len - 1] == '\r') str[--str_len] = '\0';
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Log error message. */
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("%s", str);
|
2021-08-07 09:42:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
JSON_GETTER(Boolean, bool, boolean);
|
|
|
|
JSON_SETTER(Boolean, bool, boolean);
|
|
|
|
|
|
|
|
JSON_GETTER(Integer, int, int, INT32_MIN, INT32_MAX);
|
|
|
|
JSON_SETTER(Integer, int, int, INT32_MIN, INT32_MAX);
|
|
|
|
|
|
|
|
JSON_GETTER(String, const char*, string);
|
|
|
|
JSON_SETTER(String, const char*, string);
|
|
|
|
|
|
|
|
/* Special handling for JSON arrays. */
|
|
|
|
|
|
|
|
struct json_object *jsonGetArray(const struct json_object *obj, const char *path)
|
|
|
|
{
|
|
|
|
struct json_object *ret = NULL, *child = jsonGetObjectByPath(obj, path, NULL);
|
|
|
|
if (child && jsonValidateArray(child)) ret = child;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool jsonSetArray(const struct json_object *obj, const char *path, struct json_object *value)
|
|
|
|
{
|
|
|
|
if (!obj || !path || !*path || !jsonValidateArray(value))
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Invalid parameters!");
|
2021-08-07 09:42:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
bool ret = false;
|
|
|
|
struct json_object *parent_obj = NULL;
|
|
|
|
char *key = NULL;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Get parent JSON object. */
|
|
|
|
parent_obj = jsonGetObjectByPath(obj, path, &key);
|
|
|
|
if (!parent_obj)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("Failed to retrieve parent JSON object! (\"%s\").", path);
|
2021-08-07 09:42:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Set new JSON array. */
|
|
|
|
if (json_object_object_add(parent_obj, key, value) != 0)
|
|
|
|
{
|
2022-07-12 17:34:49 +01:00
|
|
|
LOG_MSG_ERROR("json_object_object_add failed! (\"%s\").", path);
|
2021-08-07 09:42:03 +01:00
|
|
|
goto end;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
/* Update return value. */
|
|
|
|
ret = true;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
end:
|
|
|
|
if (key) free(key);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-08-07 09:42:03 +01:00
|
|
|
return ret;
|
|
|
|
}
|