/** @file
This file is cloned from DMTF libredfish library tag v1.0.0 and maintained
by EDKII.
//----------------------------------------------------------------------------
// Copyright Notice:
// Copyright 2017 Distributed Management Task Force, Inc. All rights reserved.
// License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libredfish/LICENSE.md
//----------------------------------------------------------------------------
Copyright (c) 2019, Intel Corporation. All rights reserved.
(C) Copyright 2021 Hewlett Packard Enterprise Development LP
Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
static redfishPayload *
getOpResult (
redfishPayload *payload,
const char *propName,
const char *op,
const char *value,
EFI_HTTP_STATUS_CODE **StatusCode
);
static redfishPayload *
collectionEvalOp (
redfishPayload *payload,
const char *propName,
const char *op,
const char *value,
EFI_HTTP_STATUS_CODE **StatusCode
);
static redfishPayload *
arrayEvalOp (
redfishPayload *payload,
const char *propName,
const char *op,
const char *value,
EFI_HTTP_STATUS_CODE **StatusCode
);
static redfishPayload *
createCollection (
redfishService *service,
size_t count,
redfishPayload **payloads
);
static json_t *
json_object_get_by_index (
json_t *json,
size_t index
);
bool
isPayloadCollection (
redfishPayload *payload
)
{
json_t *members;
json_t *count;
if (!payload || !json_is_object (payload->json)) {
return false;
}
members = json_object_get (payload->json, "Members");
count = json_object_get (payload->json, "Members@odata.count");
return ((members != NULL) && (count != NULL));
}
size_t
getCollectionSize (
redfishPayload *payload
)
{
json_t *members;
json_t *count;
if (!payload || !json_is_object (payload->json)) {
return 0;
}
members = json_object_get (payload->json, "Members");
count = json_object_get (payload->json, "Members@odata.count");
if (!members || !count) {
return 0;
}
return (size_t)json_integer_value (count);
}
bool
isPayloadArray (
redfishPayload *payload
)
{
if (!payload || !json_is_array (payload->json)) {
return false;
}
return true;
}
char *
payloadToString (
redfishPayload *payload,
bool prettyPrint
)
{
size_t flags = 0;
if (!payload) {
return NULL;
}
if (prettyPrint) {
flags = JSON_INDENT (2);
}
return json_dumps (payload->json, flags);
}
redfishPayload *
createRedfishPayload (
json_t *value,
redfishService *service
)
{
redfishPayload *payload;
payload = (redfishPayload *)malloc (sizeof (redfishPayload));
if (payload != NULL) {
payload->json = value;
payload->service = service;
}
return payload;
}
redfishPayload *
getPayloadByNodeName (
redfishPayload *payload,
const char *nodeName,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
json_t *value;
json_t *odataId;
const char *uri;
if (!payload || !nodeName || (StatusCode == NULL)) {
return NULL;
}
*StatusCode = NULL;
value = json_object_get (payload->json, nodeName);
if (value == NULL) {
return NULL;
}
json_incref (value);
if (json_object_size (value) == 1) {
odataId = json_object_get (value, "@odata.id");
if (odataId != NULL) {
json_incref (odataId);
uri = json_string_value (odataId);
json_decref (value);
value = getUriFromService (payload->service, uri, StatusCode);
json_decref (odataId);
if ((value == NULL) || (*StatusCode == NULL)) {
return NULL;
}
}
}
if ((*StatusCode == NULL) || ((**StatusCode >= HTTP_STATUS_200_OK) && (**StatusCode <= HTTP_STATUS_206_PARTIAL_CONTENT))) {
if (json_is_string (value)) {
odataId = json_object ();
json_object_set (odataId, nodeName, value);
json_decref (value);
value = odataId;
}
}
return createRedfishPayload (value, payload->service);
}
redfishPayload *
getPayloadByIndex (
redfishPayload *payload,
size_t index,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
json_t *value = NULL;
json_t *odataId;
const char *uri;
BOOLEAN FromServerFlag = FALSE;
if (!payload || (StatusCode == NULL)) {
return NULL;
}
*StatusCode = NULL;
if (isPayloadCollection (payload)) {
redfishPayload *members = getPayloadByNodeName (payload, "Members", StatusCode);
if (((*StatusCode == NULL) && (members == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
return members;
}
if (*StatusCode != NULL) {
//
// The Payload (members) are retrived from server.
//
FreePool (*StatusCode);
*StatusCode = NULL;
FromServerFlag = TRUE;
}
redfishPayload *ret = getPayloadByIndex (members, index, StatusCode);
if ((*StatusCode == NULL) && (ret != NULL) && FromServerFlag) {
//
// In such a case, the Redfish resource is parsed from the input payload (members) directly.
// Since the members are retrived from server, we still return HTTP_STATUS_200_OK.
//
*StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE));
if (*StatusCode == NULL) {
ret = NULL;
} else {
**StatusCode = HTTP_STATUS_200_OK;
}
}
cleanupPayload (members);
return ret;
}
if (json_is_array (payload->json)) {
//
// The valid range for index is from 0 to the return value of json_array_size() minus 1
//
value = json_array_get (payload->json, index);
} else if (json_is_object (payload->json)) {
value = json_object_get_by_index (payload->json, index);
}
if (value == NULL) {
return NULL;
}
json_incref (value);
if (json_object_size (value) == 1) {
odataId = json_object_get (value, "@odata.id");
if (odataId != NULL) {
uri = json_string_value (odataId);
json_decref (value);
value = getUriFromService (payload->service, uri, StatusCode);
if (value == NULL) {
return NULL;
}
}
}
return createRedfishPayload (value, payload->service);
}
redfishPayload *
getPayloadForPath (
redfishPayload *payload,
redPathNode *redpath,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
redfishPayload *ret = NULL;
redfishPayload *tmp;
if (!payload || !redpath || (StatusCode == NULL)) {
return NULL;
}
*StatusCode = NULL;
BOOLEAN FromServerFlag = FALSE;
if (redpath->nodeName) {
ret = getPayloadByNodeName (payload, redpath->nodeName, StatusCode);
if (((*StatusCode == NULL) && (ret == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
//
// Any error happen, return directly.
//
return ret;
}
} else if (redpath->isIndex) {
ASSERT (redpath->index >= 1);
ret = getPayloadByIndex (payload, redpath->index - 1, StatusCode);
if (((*StatusCode == NULL) && (ret == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
//
// Any error happen, return directly.
//
return ret;
}
} else if (redpath->op) {
ret = getOpResult (payload, redpath->propName, redpath->op, redpath->value, StatusCode);
if (((*StatusCode == NULL) && (ret == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
//
// Any error happen, return directly.
//
return ret;
}
} else {
return NULL;
}
if ((redpath->next == NULL) || (ret == NULL)) {
return ret;
} else {
if (*StatusCode != NULL) {
FreePool (*StatusCode);
*StatusCode = NULL;
FromServerFlag = TRUE;
}
tmp = getPayloadForPath (ret, redpath->next, StatusCode);
if ((*StatusCode == NULL) && (tmp != NULL) && FromServerFlag) {
//
// In such a case, the Redfish resource is parsed from the input payload (ret) directly.
// Since the ret are retrived from server, we still return HTTP_STATUS_200_OK.
//
*StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE));
if (*StatusCode == NULL) {
tmp = NULL;
} else {
**StatusCode = HTTP_STATUS_200_OK;
}
}
cleanupPayload (ret);
return tmp;
}
}
redfishPayload *
getPayloadForPathString (
redfishPayload *payload,
const char *string,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
redPathNode *redpath;
redfishPayload *ret;
if (!string || (StatusCode == NULL)) {
return NULL;
}
*StatusCode = NULL;
redpath = parseRedPath (string);
if (redpath == NULL) {
return NULL;
}
ret = getPayloadForPath (payload, redpath, StatusCode);
cleanupRedPath (redpath);
return ret;
}
redfishPayload *
patchPayload (
redfishPayload *target,
redfishPayload *payload,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
json_t *json;
char *content;
char *uri;
if (!target || !payload || (StatusCode == NULL)) {
return NULL;
}
*StatusCode = NULL;
json = json_object_get (target->json, "@odata.id");
if (json == NULL) {
return NULL;
}
uri = strdup (json_string_value (json));
content = json_dumps (payload->json, 0);
json_decref (json);
json = patchUriFromService (target->service, uri, content, StatusCode);
free (uri);
free (content);
if (json == NULL) {
return NULL;
}
return createRedfishPayload (json, target->service);
}
redfishPayload *
postContentToPayload (
redfishPayload *target,
const char *data,
size_t dataSize,
const char *contentType,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
json_t *json;
char *uri;
if (!target || !data || (StatusCode == NULL)) {
return NULL;
}
*StatusCode = NULL;
json = json_object_get (target->json, "@odata.id");
if (json == NULL) {
json = json_object_get (target->json, "target");
if (json == NULL) {
return NULL;
}
}
uri = strdup (json_string_value (json));
json = postUriFromService (target->service, uri, data, dataSize, contentType, StatusCode);
free (uri);
if (json == NULL) {
return NULL;
}
return createRedfishPayload (json, target->service);
}
redfishPayload *
postPayload (
redfishPayload *target,
redfishPayload *payload,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
char *content;
redfishPayload *ret;
if (!target || !payload || (StatusCode == NULL)) {
return NULL;
}
*StatusCode = NULL;
if (!json_is_object (payload->json)) {
return NULL;
}
content = payloadToString (payload, false);
ret = postContentToPayload (target, content, strlen (content), NULL, StatusCode);
free (content);
return ret;
}
void
cleanupPayload (
redfishPayload *payload
)
{
if (!payload) {
return;
}
json_decref (payload->json);
// Don't free payload->service, let the caller handle cleaning up the service
free (payload);
}
static redfishPayload *
getOpResult (
redfishPayload *payload,
const char *propName,
const char *op,
const char *value,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
const char *propStr;
json_t *stringProp;
bool ret = false;
redfishPayload *prop;
long long intVal, intPropVal;
json_type jsonType;
if (isPayloadCollection (payload)) {
return collectionEvalOp (payload, propName, op, value, StatusCode);
}
if (isPayloadArray (payload)) {
return arrayEvalOp (payload, propName, op, value, StatusCode);
}
prop = getPayloadByNodeName (payload, propName, StatusCode);
if (((*StatusCode == NULL) && (prop == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
return prop;
}
stringProp = prop->json;
jsonType = JsonGetType (prop->json);
switch (jsonType) {
case JSON_OBJECT:
stringProp = json_object_get (prop->json, propName);
case JSON_STRING:
if (strcmp (op, "=") == 0) {
propStr = json_string_value (stringProp);
if (propStr == NULL) {
cleanupPayload (prop);
return NULL;
}
ret = (strcmp (propStr, value) == 0);
} else if (strcmp (op, "~") == 0) {
propStr = json_string_value (stringProp);
if (propStr == NULL) {
cleanupPayload (prop);
return NULL;
}
ret = (strcasecmp (propStr, value) == 0);
}
break;
case JSON_TRUE:
if (strcmp (op, "=") == 0) {
ret = (strcmp (value, "true") == 0);
}
break;
case JSON_FALSE:
if (strcmp (op, "=") == 0) {
ret = (strcmp (value, "false") == 0);
}
break;
case JSON_INTEGER:
intPropVal = json_integer_value (prop->json);
intVal = strtoll (value, NULL, 0);
if (strcmp (op, "=") == 0) {
ret = (intPropVal == intVal);
} else if (strcmp (op, "<") == 0) {
ret = (intPropVal < intVal);
} else if (strcmp (op, ">") == 0) {
ret = (intPropVal > intVal);
} else if (strcmp (op, "<=") == 0) {
ret = (intPropVal <= intVal);
} else if (strcmp (op, ">=") == 0) {
ret = (intPropVal >= intVal);
}
break;
default:
break;
}
cleanupPayload (prop);
if (ret) {
return payload;
} else {
return NULL;
}
}
static redfishPayload *
collectionEvalOp (
redfishPayload *payload,
const char *propName,
const char *op,
const char *value,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
redfishPayload *ret;
redfishPayload *tmp;
redfishPayload *members;
redfishPayload **valid;
size_t validMax;
size_t validCount = 0;
size_t i;
validMax = getCollectionSize (payload);
if (validMax == 0) {
return NULL;
}
valid = (redfishPayload **)calloc (validMax, sizeof (redfishPayload *));
if (valid == NULL) {
return NULL;
}
/*Technically getPayloadByIndex would do this, but this optimizes things*/
members = getPayloadByNodeName (payload, "Members", StatusCode);
if (((*StatusCode == NULL) && (members == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
free (valid);
return members;
}
for (i = 0; i < validMax; i++) {
if (*StatusCode != NULL) {
FreePool (*StatusCode);
*StatusCode = NULL;
}
tmp = getPayloadByIndex (members, i, StatusCode);
if (((*StatusCode == NULL) && (tmp == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
free (valid);
return tmp;
}
if (*StatusCode != NULL) {
FreePool (*StatusCode);
*StatusCode = NULL;
}
valid[validCount] = getOpResult (tmp, propName, op, value, StatusCode);
/*
if ((*StatusCode == NULL && valid[validCount] == NULL) ||
(*StatusCode != NULL && (**StatusCode < HTTP_STATUS_200_OK || **StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))) {
return valid[validCount];
}
*/
if (valid[validCount] != NULL) {
validCount++;
} else {
cleanupPayload (tmp);
}
}
cleanupPayload (members);
if (validCount == 0) {
ret = NULL;
} else if (validCount == 1) {
ret = valid[0];
} else {
ret = createCollection (payload->service, validCount, valid);
}
free (valid);
return ret;
}
static redfishPayload *
arrayEvalOp (
redfishPayload *payload,
const char *propName,
const char *op,
const char *value,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
redfishPayload *ret;
redfishPayload *tmp;
redfishPayload **valid;
size_t validMax;
size_t validCount = 0;
size_t i;
validMax = json_array_size (payload->json);
if (validMax == 0) {
return NULL;
}
valid = (redfishPayload **)calloc (validMax, sizeof (redfishPayload *));
if (valid == NULL) {
return NULL;
}
for (i = 0; i < validMax; i++) {
if (*StatusCode != NULL) {
FreePool (*StatusCode);
*StatusCode = NULL;
}
tmp = getPayloadByIndex (payload, i, StatusCode);
if (((*StatusCode == NULL) && (tmp == NULL)) ||
((*StatusCode != NULL) && ((**StatusCode < HTTP_STATUS_200_OK) || (**StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))))
{
return tmp;
}
if (*StatusCode != NULL) {
FreePool (*StatusCode);
*StatusCode = NULL;
}
valid[validCount] = getOpResult (tmp, propName, op, value, StatusCode);
/*
if ((*StatusCode == NULL && valid[validCount] == NULL) ||
(*StatusCode != NULL && (**StatusCode < HTTP_STATUS_200_OK || **StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT))) {
return valid[validCount];
}
*/
if (valid[validCount] != NULL) {
validCount++;
} else {
cleanupPayload (tmp);
}
}
if (validCount == 0) {
free (valid);
return NULL;
}
if (validCount == 1) {
ret = valid[0];
free (valid);
return ret;
} else {
ret = createCollection (payload->service, validCount, valid);
free (valid);
return ret;
}
}
static redfishPayload *
createCollection (
redfishService *service,
size_t count,
redfishPayload **payloads
)
{
redfishPayload *ret;
json_t *collectionJson = json_object ();
json_t *jcount = json_integer ((json_int_t)count);
json_t *members = json_array ();
size_t i;
if (!collectionJson) {
return NULL;
}
if (!members) {
json_decref (collectionJson);
return NULL;
}
json_object_set (collectionJson, "Members@odata.count", jcount);
json_decref (jcount);
for (i = 0; i < count; i++) {
json_array_append (members, payloads[i]->json);
cleanupPayload (payloads[i]);
}
json_object_set (collectionJson, "Members", members);
json_decref (members);
ret = createRedfishPayload (collectionJson, service);
return ret;
}
static json_t *
json_object_get_by_index (
json_t *json,
size_t index
)
{
void *iter;
size_t i;
iter = json_object_iter (json);
for (i = 0; i < index; i++) {
iter = json_object_iter_next (json, iter);
if (iter == NULL) {
break;
}
}
if (iter == NULL) {
return NULL;
}
return json_object_iter_value (iter);
}
/* vim: set tabstop=4 shiftwidth=4 expandtab: */