/** @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
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 = prop->json->type;
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))) {
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))) {
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)
{
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* 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: */