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