/** @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.<BR>
|
(C) Copyright 2021 Hewlett Packard Enterprise Development LP<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
#include <redfishPayload.h>
|
|
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: */
|