/** @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: */