This patch is sent in behalf of Nickle Wang. > -----Original Message----- > From: devel@edk2.groups.io [mailto:devel@edk2.groups.io] On Behalf Of > Abner Chang > Sent: Thursday, October 28, 2021 9:58 AM > To: devel@edk2.groups.io > Cc: Wang, Nickle (HPS SW) <nickle.w...@hpe.com>; Liming Gao > <gaolim...@byosoft.com.cn> > Subject: [edk2-devel] [PATCH] edk2-staging/RedfishClientPkg: RedfishLib > > (This one is the same as RedfishLib under RedfishPkg. > The one under RedfishPkg will be removed because > RedfishLib is used by EDKII feature drivers which is belong > to EDK2 Redfish client implementation)
Reviewed-by: Abner Chang <abner.ch...@hpe.com> > > EDK2 port of DMTF libredfish project. We clone the necessary files > from open source project libredfish (https://github.com/DMTF/ > libredfish) tag v1.0.0 and revise it to incorporate with edk2 > firmware code base. > > The reason of cloning the necessary files instead of using extern > submodule of libredfish project: > libredfish as a C library which is executed under Windows and > Linux. It could be binded with other programming languages such as > java and python. The library uses curl library as the communication > service with Redfish, which is not easy to be abstracted and > replaced with EFI specific protocols (e.g. EFI_REST_EX_PROTOCOL or > payload encode/decode library) and EFI data types. We had the > conversation with DMTF community and they think edk2 is a firmware > solution but not the programming language, > therefore they rejected to have edk2 as a binding to libredfish. > According to above, we decide to clone the necessary files from > libredfish modify it to incorporate with edk2. > > Signed-off-by: Nickle Wang <nickle.w...@hpe.com> > Cc: Abner Chang <abner.ch...@hpe.com> > Cc: Liming Gao <gaolim...@byosoft.com.cn> > --- > RedfishClientPkg/RedfishClientPkg.dec | 5 + > RedfishClientPkg/RedfishClientLibs.dsc.inc | 4 + > RedfishClientPkg/RedfishClientPkg.dsc | 3 +- > .../PrivateLibrary/RedfishLib/RedfishLib.inf | 62 + > .../PrivateLibrary/RedfishLib/RedfishMisc.h | 82 + > .../edk2libredfish/include/redfish.h | 24 + > .../edk2libredfish/include/redfishPayload.h | 39 + > .../edk2libredfish/include/redfishService.h | 101 ++ > .../edk2libredfish/include/redpath.h | 42 + > .../PrivateLibrary/RedfishLib/RedfishLib.c | 993 ++++++++++++ > .../PrivateLibrary/RedfishLib/RedfishMisc.c | 201 +++ > .../RedfishLib/edk2libredfish/src/payload.c | 732 +++++++++ > .../RedfishLib/edk2libredfish/src/redpath.c | 192 +++ > .../RedfishLib/edk2libredfish/src/service.c | 1396 +++++++++++++++++ > 14 files changed, 3875 insertions(+), 1 deletion(-) > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.h > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish.h > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfishPa > yload.h > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfishSe > rvice.h > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redpath. > h > create mode 100644 RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.c > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.c > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/payload.c > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/redpath.c > create mode 100644 > RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c > > diff --git a/RedfishClientPkg/RedfishClientPkg.dec > b/RedfishClientPkg/RedfishClientPkg.dec > index b965f915a84..09df062dd36 100644 > --- a/RedfishClientPkg/RedfishClientPkg.dec > +++ b/RedfishClientPkg/RedfishClientPkg.dec > @@ -22,6 +22,11 @@ > [LibraryClasses] > RedfishFeatureUtilityLib|Include/Library/RedfishFeatureUtilityLib.h > > +[LibraryClasses.Common.Private] > + ## @libraryclass Redfish Helper Library > + # Library provides Redfish helper functions. > + RedfishLib|PrivateInclude/Library/RedfishLib.h > + > [Protocols] > ## Include/Protocol/EdkIIRedfishFeature.h > gEdkIIRedfishFeatureProtocolGuid = { 0x785CC694, 0x4930, 0xEFBF, > { 0x2A, 0xCB, 0xA4, 0xB6, 0xA1, 0xCC, 0xAA, 0x34 } } > diff --git a/RedfishClientPkg/RedfishClientLibs.dsc.inc > b/RedfishClientPkg/RedfishClientLibs.dsc.inc > index a5ae73cac7f..4655dd7081b 100644 > --- a/RedfishClientPkg/RedfishClientLibs.dsc.inc > +++ b/RedfishClientPkg/RedfishClientLibs.dsc.inc > @@ -14,6 +14,10 @@ > !include RedfishClientPkg/RedfishJsonStructureLib.dsc.inc > !endif > > + NetLib|NetworkPkg/Library/DxeNetLib/DxeNetLib.inf > + HttpLib|NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf > + RedfishLib|RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > > RedfishFeatureUtilityLib|RedfishClientPkg/Library/RedfishFeatureUtilityLib/ > RedfishFeatureUtilityLib.inf > > RedfishPlatformConfigLib|RedfishPkg/Library/RedfishPlatformConfigLib/Red > fishPlatformConfigLib.inf > + > RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/ > RedfishContentCodingLibNull.inf > > diff --git a/RedfishClientPkg/RedfishClientPkg.dsc > b/RedfishClientPkg/RedfishClientPkg.dsc > index 00a963eaff1..ddc8cef1fd8 100644 > --- a/RedfishClientPkg/RedfishClientPkg.dsc > +++ b/RedfishClientPkg/RedfishClientPkg.dsc > @@ -48,5 +48,6 @@ > [Components] > > > RedfishClientPkg/Library/RedfishFeatureUtilityLib/RedfishFeatureUtilityLib.i > nf > + RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > > - !include RedfishClientPkg/RedfishClient.dsc.inc > + !include RedfishClientPkg/RedfishClient.dsc.inc > \ No newline at end of file > diff --git a/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > new file mode 100644 > index 00000000000..a54e397d190 > --- /dev/null > +++ b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > @@ -0,0 +1,62 @@ > +## @file > +# RedfishLib Library implementation. > +# > +# 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 > +# > +## > + > +[Defines] > + INF_VERSION = 0x0001001b > + BASE_NAME = DxeRedfishLib > + FILE_GUID = 9C2CA9CF-4F79-11E8-A7D1-8CDCD426C973 > + MODULE_TYPE = DXE_DRIVER > + VERSION_STRING = 1.0 > + LIBRARY_CLASS = RedfishLib| DXE_DRIVER UEFI_APPLICATION > UEFI_DRIVER > + > +# > +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 RISCV64 > +# > + > +[Sources] > + edk2libredfish/src/redpath.c > + edk2libredfish/src/service.c > + edk2libredfish/src/payload.c > + edk2libredfish/include/redfish.h > + edk2libredfish/include/redfishPayload.h > + edk2libredfish/include/redfishService.h > + edk2libredfish/include/redpath.h > + RedfishLib.c > + RedfishMisc.h > + RedfishMisc.c > + > +[Packages] > + MdePkg/MdePkg.dec > + MdeModulePkg/MdeModulePkg.dec > + NetworkPkg/NetworkPkg.dec > + RedfishPkg/RedfishPkg.dec > + RedfishClientPkg/RedfishClientPkg.dec > + > +[LibraryClasses] > + BaseLib > + BaseMemoryLib > + DebugLib > + JsonLib > + HttpLib > + MemoryAllocationLib > + NetLib > + RedfishContentCodingLib > + RedfishCrtLib > + UefiBootServicesTableLib > + UefiLib > + > +[Protocols] > + gEfiRestExServiceBindingProtocolGuid ## Consumed > + gEfiRestExProtocolGuid ## Consumed > + gEdkIIRedfishCredentialProtocolGuid ## Consumed > + > +[BuildOptions] > + MSFT:*_*_*_CC_FLAGS = /U_WIN32 /UWIN64 /U_MSC_VER > + GCC:*_*_*_CC_FLAGS = -Wno-unused-function -Wno-unused-but-set- > variable > diff --git a/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.h > b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.h > new file mode 100644 > index 00000000000..d01a433d1aa > --- /dev/null > +++ b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.h > @@ -0,0 +1,82 @@ > +/** @file > + Internal Functions for RedfishLib. > + > + 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 > + > +**/ > + > +#ifndef DXE_REDFISH_MISC_LIB_H_ > +#define DXE_REDFISH_MISC_LIB_H_ > + > +#include <Library/BaseLib.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/DebugLib.h> > +#include <Library/JsonLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/PrintLib.h> > +#include <Library/RedfishLib.h> > +#include <Library/UefiLib.h> > +#include <Protocol/EdkIIRedfishCredential.h> > +#include <redfish.h> > + > +#define ARRAY_SIZE(Array) (sizeof (Array) / sizeof ((Array)[0])) > + > +/** > + Creates a REDFISH_SERVICE which can be later used to access the Redfish > resources. > + > + This function will configure REST EX child according to parameters > described in > + Redfish network host interface in SMBIOS type 42 record. The service > enumerator will also > + handle the authentication flow automatically if HTTP basic auth or Redfish > session > + login is configured to use. > + > + @param[in] RedfishConfigServiceInfo Redfish service information the EFI > Redfish > + feature driver communicates with. > + @param[in] AuthMethod None, HTTP basic auth, or Redfish session login. > + @param[in] UserId User Name used for authentication. > + @param[in] Password Password used for authentication. > + > + @return New created Redfish service, or NULL if error happens. > + > +**/ > +REDFISH_SERVICE > +RedfishCreateLibredfishService ( > + IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo, > + IN EDKII_REDFISH_AUTH_METHOD AuthMethod, > + IN CHAR8 *UserId, > + IN CHAR8 *Password > + ); > + > +/** > + Retrieve platform's Redfish authentication information. > + > + This functions returns the Redfish authentication method together with > the user > + Id and password. > + For AuthMethodNone, UserId and Password will point to NULL which > means authentication > + is not required to access the Redfish service. > + For AuthMethodHttpBasic, the UserId and Password could be used for > + HTTP header authentication as defined by RFC7235. For > AuthMethodRedfishSession, > + the UserId and Password could be used for Redfish session login as > defined by > + Redfish API specification (DSP0266). > + > + Callers are responsible for freeing the returned string storage pointed by > UserId > + and Password. > + > + @param[out] AuthMethod Type of Redfish authentication method. > + @param[out] UserId The pointer to store the returned UserId > string. > + @param[out] Password The pointer to store the returned Password > string. > + > + @retval EFI_SUCCESS Get the authentication information > successfully. > + @retval EFI_INVALID_PARAMETER AuthMethod or UserId or Password is > NULL. > + @retval EFI_UNSUPPORTED Unsupported authentication method is > found. > +**/ > +EFI_STATUS > +RedfishGetAuthInfo ( > + OUT EDKII_REDFISH_AUTH_METHOD *AuthMethod, > + OUT CHAR8 **UserId, > + OUT CHAR8 **Password > + ); > + > +#endif > diff --git > a/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > .h > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > .h > new file mode 100644 > index 00000000000..de1feb22fbc > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > .h > @@ -0,0 +1,24 @@ > +/** @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 > + > +**/ > +#ifndef LIBREDFISH_REDFISH_H_ > +#define LIBREDFISH_REDFISH_H_ > + > +#include <redfishService.h> > +#include <redfishPayload.h> > +#include <redpath.h> > + > +#endif > diff --git > a/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Payload.h > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Payload.h > new file mode 100644 > index 00000000000..43149f3c895 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Payload.h > @@ -0,0 +1,39 @@ > +/** @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 > + > +**/ > +#ifndef LIBREDFISH_REDFISH_PAYLOAD_H_ > +#define LIBREDFISH_REDFISH_PAYLOAD_H_ > + > +#include <Include/Library/RedfishCrtLib.h> > + > +#include <jansson.h> > +#include <redfishService.h> > +#include <redpath.h> > + > +redfishPayload* createRedfishPayload(json_t* value, redfishService* > service); > +redfishPayload* getPayloadByNodeName(redfishPayload* payload, const > char* nodeName, EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* getPayloadByIndex(redfishPayload* payload, size_t index, > EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* getPayloadForPath(redfishPayload* payload, > redPathNode* redpath, EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* getPayloadForPathString(redfishPayload* payload, const > char* string, EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* patchPayload(redfishPayload* target, redfishPayload* > payload, EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* postContentToPayload(redfishPayload* target, const > char* data, size_t dataSize, const char* contentType, > EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* postPayload(redfishPayload* target, redfishPayload* > payload, EFI_HTTP_STATUS_CODE** StatusCode); > +void cleanupPayload(redfishPayload* payload); > +bool isPayloadCollection (redfishPayload *Payload); > +size_t getCollectionSize(redfishPayload* payload); > +redfishPayload* getPayloadByIndex (redfishPayload* payload, size_t index, > EFI_HTTP_STATUS_CODE** StatusCode); > + > +#endif > diff --git > a/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Service.h > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Service.h > new file mode 100644 > index 00000000000..0215caccfcc > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Service.h > @@ -0,0 +1,101 @@ > +/** @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 > + > +**/ > + > +#ifndef LIBREDFISH_REDFISH_SERVICE_H_ > +#define LIBREDFISH_REDFISH_SERVICE_H_ > + > +#include <IndustryStandard/Http11.h> > + > +#include <Library/BaseLib.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/DebugLib.h> > +#include <Library/HttpLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/NetLib.h> > +#include <Library/RedfishContentCodingLib.h> > +#include <Library/UefiRuntimeServicesTableLib.h> > +#include <Library/UefiBootServicesTableLib.h> > + > +#include <Include/Library/RedfishCrtLib.h> > + > +#include <Protocol/EdkIIRedfishConfigHandler.h> > +#include <Protocol/RestEx.h> > + > +#include <jansson.h> > + > +typedef struct { > + char* host; > + json_t* versions; > + unsigned int flags; > + char* sessionToken; > + char* basicAuthStr; > + // > + // point to the <HOST> part in above "host" field, which will be put into > + // the "Host" header of HTTP request message. > + // > + char* HostHeaderValue; > + EFI_REST_EX_PROTOCOL *RestEx; > +} redfishService; > + > +typedef struct { > + json_t* json; > + redfishService* service; > +} redfishPayload; > + > +#define REDFISH_AUTH_BASIC 0 > +#define REDFISH_AUTH_BEARER_TOKEN 1 > +#define REDFISH_AUTH_SESSION 2 > + > +#define REDFISH_HTTP_RESPONSE_TIMEOUT 5000 /// 5 seconds in uints > of millisecond. > + > +/// > +/// Library class public defines > +/// > +#define HTTP_FLAG L"http://" > +#define HTTPS_FLAG L"https://" > + > +/// > +/// The redfish first URL should be "/redfish/v1/", while we use > "/redfish/v1" here without "/" > +/// in the end is to avoid the 301 Perment redirect response from Redfish > profile simulator. > +/// > +#define REDFISH_FIRST_URL L"/redfish/v1" > + > +typedef struct { > + unsigned int authType; > + union { > + struct { > + char* username; > + char* password; > + } userPass; > + struct { > + char* token; > + } authToken; > + } authCodes; > +} enumeratorAuthentication; > + > +//Values for flags > +#define REDFISH_FLAG_SERVICE_NO_VERSION_DOC 0x00000001 //The > Redfish Service lacks the version document (in violation of the Redfish spec) > +redfishService* > createServiceEnumerator(REDFISH_CONFIG_SERVICE_INFORMATION > *RedfishConfigServiceInfo, const char* rootUri, enumeratorAuthentication* > auth, unsigned int flags); > +json_t* getUriFromService(redfishService* service, const char* uri, > EFI_HTTP_STATUS_CODE** StatusCode); > +json_t* patchUriFromService(redfishService* service, const char* uri, const > char* content, EFI_HTTP_STATUS_CODE** StatusCode); > +json_t* postUriFromService(redfishService* service, const char* uri, const > char* content, size_t contentLength, const char* contentType, > EFI_HTTP_STATUS_CODE** StatusCode); > +json_t* deleteUriFromService(redfishService* service, const char* uri, > EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* getRedfishServiceRoot(redfishService* service, const > char* version, EFI_HTTP_STATUS_CODE** StatusCode); > +redfishPayload* getPayloadByPath(redfishService* service, const char* > path, EFI_HTTP_STATUS_CODE** StatusCode); > +void cleanupServiceEnumerator(redfishService* service); > + > +#endif > diff --git > a/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redpat > h.h > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redpat > h.h > new file mode 100644 > index 00000000000..24413a648a6 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redpat > h.h > @@ -0,0 +1,42 @@ > +/** @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 > + > +**/ > +#ifndef LIBREDFISH_REDPATH_H_ > +#define LIBREDFISH_REDPATH_H_ > + > +#include <Include/Library/RedfishCrtLib.h> > + > +#include <jansson.h> > + > +typedef struct _redPathNode > +{ > + bool isRoot; > + bool isIndex; > + > + char* version; > + char* nodeName; > + size_t index; > + char* op; > + char* propName; > + char* value; > + > + struct _redPathNode* next; > +} redPathNode; > + > +redPathNode* parseRedPath(const char* path); > +void cleanupRedPath(redPathNode* node); > + > +#endif > diff --git a/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.c > b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.c > new file mode 100644 > index 00000000000..18aa4646e86 > --- /dev/null > +++ b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.c > @@ -0,0 +1,993 @@ > +/** @file > + Provides a set of utility APIs that allow to create/read/update/delete > + (CRUD) Redfish resources and provide basic query. > + > + 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 "RedfishMisc.h" > + > +/** > + This function uses REST EX protocol provided in RedfishConfigServiceInfo. > + The service enumerator will also handle the authentication flow > automatically > + if HTTP basic auth or Redfish session login is configured to use. > + > + Callers are responsible for freeing the returned service by > RedfishCleanupService(). > + > + @param[in] RedfishConfigServiceInfo Redfish service information the EFI > Redfish > + feature driver communicates with. > + > + @return New created Redfish Service, or NULL if error happens. > + > +**/ > +REDFISH_SERVICE > +EFIAPI > +RedfishCreateService ( > + IN REDFISH_CONFIG_SERVICE_INFORMATION > *RedfishConfigServiceInfo > + ) > +{ > + REDFISH_SERVICE RedfishService; > + EDKII_REDFISH_AUTH_METHOD AuthMethod; > + CHAR8 *UserId; > + CHAR8 *Password; > + EFI_STATUS Status; > + > + RedfishService = NULL; > + UserId = NULL; > + Password = NULL; > + > + // > + // Check Input Parameters. > + // > + if (RedfishConfigServiceInfo == NULL) { > + return NULL; > + } > + > + // > + // Get Authentication Configuration. > + // > + Status = RedfishGetAuthInfo (&AuthMethod, &UserId, &Password); > + if (EFI_ERROR (Status)) { > + goto ON_EXIT; > + } > + > + // > + // Create a redfish service node based on Redfish network host interface. > + // > + RedfishService = RedfishCreateLibredfishService ( > + RedfishConfigServiceInfo, > + AuthMethod, > + UserId, > + Password > + ); > + > +ON_EXIT: > + if (UserId != NULL) { > + FreePool (UserId); > + } > + if (Password!= NULL) { > + FreePool (Password); > + } > + > + return RedfishService; > +} > + > +/** > + Free the Service and all its related resources. > + > + @param[in] RedfishService The Service to access the Redfish > resources. > + > +**/ > +VOID > +EFIAPI > +RedfishCleanupService ( > + IN REDFISH_SERVICE RedfishService > + ) > +{ > + if (RedfishService == NULL) { > + return; > + } > + > + cleanupServiceEnumerator (RedfishService); > +} > +/** > + Create REDFISH_PAYLOAD instance in local with JSON represented > resource value and > + the Redfish Service. > + > + The returned REDFISH_PAYLOAD can be used to create or update Redfish > resource in > + server side. > + > + Callers are responsible for freeing the returned payload by > RedfishCleanupPayload(). > + > + @param[in] Value JSON Value of the redfish resource. > + @param[in] RedfishService The Service to access the Redfish > resources. > + > + @return REDFISH_PAYLOAD instance of the resource, or NULL if error > happens. > + > +**/ > +REDFISH_PAYLOAD > +EFIAPI > +RedfishCreatePayload ( > + IN EDKII_JSON_VALUE Value, > + IN REDFISH_SERVICE RedfishService > + ) > +{ > + EDKII_JSON_VALUE CopyValue; > + > + CopyValue = JsonValueClone (Value); > + return createRedfishPayload (CopyValue, RedfishService); > +} > + > +/** > + Free the RedfishPayload and all its related resources. > + > + @param[in] Payload Payload to be freed. > + > +**/ > +VOID > +EFIAPI > +RedfishCleanupPayload ( > + IN REDFISH_PAYLOAD Payload > + ) > +{ > + if (Payload == NULL) { > + return; > + } > + > + cleanupPayload ((redfishPayload *) Payload); > +} > + > +/** > + This function returns the decoded JSON value of a REDFISH_PAYLOAD. > + > + Caller doesn't need to free the returned JSON value because it will be > released > + in corresponding RedfishCleanupPayload() function. > + > + @param[in] Payload A REDFISH_PAYLOAD instance. > + > + @return Decoded JSON value of the payload. > + > +**/ > +EDKII_JSON_VALUE > +EFIAPI > +RedfishJsonInPayload ( > + IN REDFISH_PAYLOAD Payload > + ) > +{ > + if (Payload == NULL) { > + return NULL; > + } > + > + return ((redfishPayload*)Payload)->json; > +} > + > +/** > + Fill the input RedPath string with system UUID from SMBIOS table or use > the customized > + ID if FromSmbios == FALSE. > + > + This is a helper function to build a RedPath string which can be used to > address > + a Redfish resource for this computer system. The input PathString must > have a Systems > + note in format of "Systems[UUID=%g]" or "Systems[UUID~%g]" to fill the > UUID value. > + > + Example: > + Use "/v1/Systems[UUID=%g]/Bios" to build a RedPath to address the > "Bios" resource > + for this computer system. > + > + @param[in] RedPath RedPath format to be build. > + @param[in] FromSmbios Get system UUID from SMBIOS as computer > system instance ID. > + @param[in] IdString The computer system instance ID. > + > + @return Full RedPath with system UUID inside, or NULL if error happens. > + > +**/ > +CHAR8 * > +EFIAPI > +RedfishBuildPathWithSystemUuid ( > + IN CONST CHAR8 *RedPath, > + IN BOOLEAN FromSmbios, > + IN CHAR8 *IdString OPTIONAL > + ) > +{ > + UINTN BufSize; > + CHAR8* RetRedPath; > + EFI_GUID SystemUuid; > + EFI_STATUS Status; > + > + if (RedPath == NULL) { > + return NULL; > + } > + > + // > + // Find system UUID from SMBIOS table. > + // > + if (FromSmbios) { > + Status = NetLibGetSystemGuid(&SystemUuid); > + if (EFI_ERROR (Status)) { > + return NULL; > + } > + // AsciiStrLen ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") = 36 > + BufSize = AsciiStrSize (RedPath) + AsciiStrLen ("XXXXXXXX-XXXX-XXXX- > XXXX-XXXXXXXXXXXX"); > + } else { > + BufSize = AsciiStrSize (RedPath) + AsciiStrLen (IdString); > + } > + > + RetRedPath = AllocateZeroPool (BufSize); > + if (RetRedPath == NULL) { > + return NULL; > + } > + if (FromSmbios) { > + AsciiSPrint (RetRedPath, BufSize, RedPath, &SystemUuid); > + } else { > + AsciiSPrint (RetRedPath, BufSize, RedPath, IdString); > + } > + return RetRedPath; > +} > +/** > + Get a redfish response addressed by a RedPath string, including HTTP > StatusCode, Headers > + and Payload which record any HTTP response messages. > + > + Callers are responsible for freeing the HTTP StatusCode, Headers and > Payload returned in > + redfish response data. > + > + @param[in] RedfishService The Service to access the Redfish > resources. > + @param[in] RedPath RedPath string to address a resource, > must > start > + from the root node. > + @param[out] RedResponse Pointer to the Redfish response data. > + > + @retval EFI_SUCCESS The opeartion is successful, indicates the > HTTP > StatusCode is not > + NULL and the value is 2XX. The > corresponding redfish > resource has > + been returned in Payload within > RedResponse. > + @retval EFI_INVALID_PARAMETER RedfishService, RedPath, or > RedResponse is NULL. > + @retval EFI_DEVICE_ERROR An unexpected system or network error > occurred. Callers can get > + more error info from returned HTTP > StatusCode, Headers > and Payload > + within RedResponse: > + 1. If the returned Payload is NULL, > indicates any error > happen. > + 2. If the returned StatusCode is NULL, > indicates any error > happen. > + 3. If the returned StatusCode is not 2XX, > indicates any error > happen. > +**/ > +EFI_STATUS > +EFIAPI > +RedfishGetByService ( > + IN REDFISH_SERVICE RedfishService, > + IN CONST CHAR8 *RedPath, > + OUT REDFISH_RESPONSE *RedResponse > + ) > +{ > + if (RedfishService == NULL || RedPath == NULL || RedResponse == NULL) > { > + return EFI_INVALID_PARAMETER; > + } > + > + ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE)); > + > + RedResponse->Payload = (REDFISH_PAYLOAD) getPayloadByPath > (RedfishService, RedPath, &(RedResponse->StatusCode)); > + > + // > + // 1. If the returned Payload is NULL, indicates any error happen. > + // 2. If the returned StatusCode is NULL, indicates any error happen. > + // > + if (RedResponse->Payload == NULL || RedResponse->StatusCode == NULL) > { > + return EFI_DEVICE_ERROR; > + } > + > + // > + // 3. If the returned StatusCode is not 2XX, indicates any error happen. > + // NOTE: If there is any error message returned from server, it will be > returned in > + // Payload within RedResponse. > + // > + if (*(RedResponse->StatusCode) < HTTP_STATUS_200_OK || \ > + *(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT) > { > + return EFI_DEVICE_ERROR; > + } > + > + return EFI_SUCCESS; > +} > +/** > + Get a redfish response addressed by URI, including HTTP StatusCode, > Headers > + and Payload which record any HTTP response messages. > + > + Callers are responsible for freeing the HTTP StatusCode, Headers and > Payload returned in > + redfish response data. > + > + @param[in] RedfishService The Service to access the URI resources. > + @param[in] Uri String to address a resource. > + @param[out] RedResponse Pointer to the Redfish response data. > + > + @retval EFI_SUCCESS The opeartion is successful, indicates the > HTTP > StatusCode is not > + NULL and the value is 2XX. The > corresponding redfish > resource has > + been returned in Payload within > RedResponse. > + @retval EFI_INVALID_PARAMETER RedfishService, RedPath, or > RedResponse is NULL. > + @retval EFI_DEVICE_ERROR An unexpected system or network error > occurred. Callers can get > + more error info from returned HTTP > StatusCode, Headers > and Payload > + within RedResponse: > + 1. If the returned Payload is NULL, > indicates any error > happen. > + 2. If the returned StatusCode is NULL, > indicates any error > happen. > + 3. If the returned StatusCode is not 2XX, > indicates any error > happen. > +**/ > +EFI_STATUS > +EFIAPI > +RedfishGetByUri ( > + IN REDFISH_SERVICE RedfishService, > + IN CONST CHAR8 *Uri, > + OUT REDFISH_RESPONSE *RedResponse > + ) > +{ > + EDKII_JSON_VALUE JsonValue; > + > + if (RedfishService == NULL || Uri == NULL || RedResponse == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE)); > + > + JsonValue = getUriFromService (RedfishService, Uri, &RedResponse- > >StatusCode); > + RedResponse->Payload = createRedfishPayload(JsonValue, > RedfishService); > + > + // > + // 1. If the returned Payload is NULL, indicates any error happen. > + // 2. If the returned StatusCode is NULL, indicates any error happen. > + // > + if (RedResponse->Payload == NULL || RedResponse->StatusCode == NULL) > { > + return EFI_DEVICE_ERROR; > + } > + > + // > + // 3. If the returned StatusCode is not 2XX, indicates any error happen. > + // NOTE: If there is any error message returned from server, it will be > returned in > + // Payload within RedResponse. > + // > + if (*(RedResponse->StatusCode) < HTTP_STATUS_200_OK || \ > + *(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT) > { > + return EFI_DEVICE_ERROR; > + } > + return EFI_SUCCESS; > +} > +/** > + Get a redfish response addressed by the input Payload and relative > RedPath string, > + including HTTP StatusCode, Headers and Payload which record any HTTP > response messages. > + > + Callers are responsible for freeing the HTTP StatusCode, Headers and > Payload returned in > + redfish response data. > + > + @param[in] Payload A existing REDFISH_PAYLOAD instance. > + @param[in] RedPath Relative RedPath string to address a > resource > inside Payload. > + @param[out] RedResponse Pointer to the Redfish response data. > + > + @retval EFI_SUCCESS The opeartion is successful: > + 1. The HTTP StatusCode is NULL and the > returned Payload > in > + RedResponse is not NULL, indicates the > Redfish resource > has > + been parsed from the input payload > directly. > + 2. The HTTP StatusCode is not NULL and the > value is 2XX, > + indicates the corresponding redfish > resource has been > returned > + in Payload within RedResponse. > + @retval EFI_INVALID_PARAMETER Payload, RedPath, or RedResponse is > NULL. > + @retval EFI_DEVICE_ERROR An unexpected system or network error > occurred. Callers can get > + more error info from returned HTTP > StatusCode, Headers > and Payload > + within RedResponse: > + 1. If the returned Payload is NULL, > indicates any error > happen. > + 2. If StatusCode is not NULL and the > returned value of > StatusCode > + is not 2XX, indicates any error happen. > +**/ > +EFI_STATUS > +EFIAPI > +RedfishGetByPayload ( > + IN REDFISH_PAYLOAD Payload, > + IN CONST CHAR8 *RedPath, > + OUT REDFISH_RESPONSE *RedResponse > + ) > +{ > + if (Payload == NULL || RedPath == NULL || RedResponse == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE)); > + > + RedResponse->Payload = (REDFISH_PAYLOAD) getPayloadForPathString > (Payload, RedPath, &(RedResponse->StatusCode)); > + > + // > + // 1. If the returned Payload is NULL, indicates any error happen. > + // > + if (RedResponse->Payload == NULL) { > + return EFI_DEVICE_ERROR; > + } > + > + // > + // 2. If StatusCode is not NULL and the returned value of StatusCode is not > 2XX, indicates any > + // error happen. > + // NOTE: If there is any error message returned from server, it will be > returned in > + // Payload within RedResponse. > + // > + if (RedResponse->StatusCode != NULL && \ > + (*(RedResponse->StatusCode) < HTTP_STATUS_200_OK || \ > + *(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT > + )) { > + return EFI_DEVICE_ERROR; > + } > + > + return EFI_SUCCESS; > +} > +/** > + Use HTTP PATCH to perform updates on pre-existing Redfish resource. > + > + This function uses the RedfishService to patch a Redfish resource > addressed by > + Uri (only the relative path is required). Changes to one or more properties > within > + the target resource are represented in the input Content, properties not > specified > + in Content won't be changed by this request. The corresponding redfish > response will > + returned, including HTTP StatusCode, Headers and Payload which record > any HTTP response > + messages. > + > + Callers are responsible for freeing the HTTP StatusCode, Headers and > Payload returned in > + redfish response data. > + > + @param[in] RedfishService The Service to access the Redfish > resources. > + @param[in] Uri Relative path to address the resource. > + @param[in] Content JSON represented properties to be > update. > + @param[out] RedResponse Pointer to the Redfish response data. > + > + @retval EFI_SUCCESS The opeartion is successful, indicates the > HTTP > StatusCode is not > + NULL and the value is 2XX. The Redfish > resource will be > returned > + in Payload within RedResponse if server > send it back in the > HTTP > + response message body. > + @retval EFI_INVALID_PARAMETER RedfishService, Uri, Content, or > RedResponse is NULL. > + @retval EFI_DEVICE_ERROR An unexpected system or network error > occurred. Callers can get > + more error info from returned HTTP > StatusCode, Headers > and Payload > + within RedResponse: > + 1. If the returned StatusCode is NULL, > indicates any error > happen. > + 2. If the returned StatusCode is not NULL > and the value is > not 2XX, > + indicates any error happen. > +**/ > +EFI_STATUS > +EFIAPI > +RedfishPatchToUri ( > + IN REDFISH_SERVICE RedfishService, > + IN CONST CHAR8 *Uri, > + IN CONST CHAR8 *Content, > + OUT REDFISH_RESPONSE *RedResponse > + ) > +{ > + EFI_STATUS Status; > + EDKII_JSON_VALUE JsonValue; > + > + Status = EFI_SUCCESS; > + JsonValue = NULL; > + > + if (RedfishService == NULL || Uri == NULL || Content == NULL || > RedResponse == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE)); > + > + JsonValue = (EDKII_JSON_VALUE) patchUriFromService ( > + RedfishService, > + Uri, > + Content, > + &(RedResponse->StatusCode) > + ); > + > + // > + // 1. If the returned StatusCode is NULL, indicates any error happen. > + // > + if (RedResponse->StatusCode == NULL) { > + Status = EFI_DEVICE_ERROR; > + goto ON_EXIT; > + } > + > + // > + // 2. If the returned StatusCode is not NULL and the value is not 2XX, > indicates any error happen. > + // NOTE: If there is any error message returned from server, it will be > returned in > + // Payload within RedResponse. > + // > + if (*(RedResponse->StatusCode) < HTTP_STATUS_200_OK || \ > + *(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT) > { > + Status = EFI_DEVICE_ERROR; > + } > + > +ON_EXIT: > + if (JsonValue != NULL) { > + RedResponse->Payload = createRedfishPayload (JsonValue, > RedfishService); > + if (RedResponse->Payload == NULL) { > + // > + // Ignore the error when create RedfishPayload, just free the JsonValue > since it's not what > + // we care about if the returned StatusCode is 2XX. > + // > + JsonValueFree (JsonValue); > + } > + } > + > + return Status; > +} > +/** > + Use HTTP PATCH to perform updates on target payload. Patch to odata.id > in Payload directly. > + > + This function uses the Payload to patch the Target. Changes to one or > more properties > + within the target resource are represented in the input Payload, > properties not specified > + in Payload won't be changed by this request. The corresponding redfish > response will > + returned, including HTTP StatusCode, Headers and Payload which record > any HTTP response > + messages. > + > + Callers are responsible for freeing the HTTP StatusCode, Headers and > Payload returned in > + redfish response data. > + > + @param[in] Target The target payload to be updated. > + @param[in] Payload Palyoad with properties to be changed. > + @param[out] RedResponse Pointer to the Redfish response data. > + > + @retval EFI_SUCCESS The opeartion is successful, indicates the > HTTP > StatusCode is not > + NULL and the value is 2XX. The Redfish > resource will be > returned > + in Payload within RedResponse if server > send it back in the > HTTP > + response message body. > + @retval EFI_INVALID_PARAMETER Target, Payload, or RedResponse is > NULL. > + @retval EFI_DEVICE_ERROR An unexpected system or network error > occurred. Callers can get > + more error info from returned HTTP > StatusCode, Headers > and Payload > + within RedResponse: > + 1. If the returned StatusCode is NULL, > indicates any error > happen. > + 2. If the returned StatusCode is not NULL > and the value is > not 2XX, > + indicates any error happen. > +**/ > +EFI_STATUS > +EFIAPI > +RedfishPatchToPayload ( > + IN REDFISH_PAYLOAD Target, > + IN REDFISH_PAYLOAD Payload, > + OUT REDFISH_RESPONSE *RedResponse > + ) > +{ > + if (Target == NULL || Payload == NULL || RedResponse == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE)); > + > + RedResponse->Payload = (REDFISH_PAYLOAD) patchPayload ( > + Target, > + Payload, > + &(RedResponse->StatusCode) > + ); > + > + // > + // 1. If the returned StatusCode is NULL, indicates any error happen. > + // > + if (RedResponse->StatusCode == NULL) { > + return EFI_DEVICE_ERROR; > + } > + > + // > + // 2. If the returned StatusCode is not NULL and the value is not 2XX, > indicates any error happen. > + // NOTE: If there is any error message returned from server, it will be > returned in > + // Payload within RedResponse. > + // > + if (*(RedResponse->StatusCode) < HTTP_STATUS_200_OK || \ > + *(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT) > { > + return EFI_DEVICE_ERROR; > + } > + > + return EFI_SUCCESS; > +} > +/** > + Use HTTP POST to create a new resource in target payload. > + > + The POST request should be submitted to the Resource Collection in which > the new resource > + is to belong. The Resource Collection is addressed by Target payload. The > Redfish may > + ignore any service controlled properties. The corresponding redfish > response will returned, > + including HTTP StatusCode, Headers and Payload which record any HTTP > response messages. > + > + Callers are responsible for freeing the HTTP StatusCode, Headers and > Payload returned in > + redfish response data. > + > + @param[in] Target Target payload of the Resource Collection. > + @param[in] Payload The new resource to be created. > + @param[out] RedResponse Pointer to the Redfish response data. > + > + @retval EFI_SUCCESS The opeartion is successful, indicates the > HTTP > StatusCode is not > + NULL and the value is 2XX. The Redfish > resource will be > returned > + in Payload within RedResponse if server > send it back in the > HTTP > + response message body. > + @retval EFI_INVALID_PARAMETER Target, Payload, or RedResponse is > NULL. > + @retval EFI_DEVICE_ERROR An unexpected system or network error > occurred. Callers can get > + more error info from returned HTTP > StatusCode, Headers > and Payload > + within RedResponse: > + 1. If the returned StatusCode is NULL, > indicates any error > happen. > + 2. If the returned StatusCode is not NULL > and the value is > not 2XX, > + indicates any error happen. > +**/ > +EFI_STATUS > +EFIAPI > +RedfishPostToPayload ( > + IN REDFISH_PAYLOAD Target, > + IN REDFISH_PAYLOAD Payload, > + OUT REDFISH_RESPONSE *RedResponse > + ) > +{ > + if (Target == NULL || Payload == NULL || RedResponse == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE)); > + > + RedResponse->Payload = (REDFISH_PAYLOAD) postPayload ( > + Target, > + Payload, > + &(RedResponse->StatusCode) > + ); > + > + // > + // 1. If the returned StatusCode is NULL, indicates any error happen. > + // > + if (RedResponse->StatusCode == NULL) { > + return EFI_DEVICE_ERROR; > + } > + > + // > + // 2. If the returned StatusCode is not NULL and the value is not 2XX, > indicates any error happen. > + // NOTE: If there is any error message returned from server, it will be > returned in > + // Payload within RedResponse. > + // > + if (*(RedResponse->StatusCode) < HTTP_STATUS_200_OK || \ > + *(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT) > { > + return EFI_DEVICE_ERROR; > + } > + > + return EFI_SUCCESS; > +} > +/** > + Use HTTP DELETE to remove a resource. > + > + This function uses the RedfishService to remove a Redfish resource which > is addressed > + by input Uri (only the relative path is required). The corresponding > redfish > response will > + returned, including HTTP StatusCode, Headers and Payload which record > any HTTP response > + messages. > + > + Callers are responsible for freeing the HTTP StatusCode, Headers and > Payload returned in > + redfish response data. > + > + @param[in] RedfishService The Service to access the Redfish > resources. > + @param[in] Uri Relative path to address the resource. > + @param[out] RedResponse Pointer to the Redfish response data. > + > + @retval EFI_SUCCESS The opeartion is successful, indicates the > HTTP > StatusCode is not > + NULL and the value is 2XX, the Redfish > resource has been > removed. > + If there is any message returned from > server, it will be > returned > + in Payload within RedResponse. > + @retval EFI_INVALID_PARAMETER RedfishService, Uri, or RedResponse is > NULL. > + @retval EFI_DEVICE_ERROR An unexpected system or network error > occurred. Callers can get > + more error info from returned HTTP > StatusCode, Headers > and Payload > + within RedResponse: > + 1. If the returned StatusCode is NULL, > indicates any error > happen. > + 2. If the returned StatusCode is not NULL > and the value is > not 2XX, > + indicates any error happen. > +**/ > +EFI_STATUS > +EFIAPI > +RedfishDeleteByUri ( > + IN REDFISH_SERVICE RedfishService, > + IN CONST CHAR8 *Uri, > + OUT REDFISH_RESPONSE *RedResponse > + ) > +{ > + EFI_STATUS Status; > + EDKII_JSON_VALUE JsonValue; > + > + Status = EFI_SUCCESS; > + JsonValue = NULL; > + > + if (RedfishService == NULL || Uri == NULL || RedResponse == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE)); > + > + JsonValue = (EDKII_JSON_VALUE) deleteUriFromService ( > + RedfishService, > + Uri, > + &(RedResponse->StatusCode) > + ); > + > + // > + // 1. If the returned StatusCode is NULL, indicates any error happen. > + // > + if (RedResponse->StatusCode == NULL) { > + Status = EFI_DEVICE_ERROR; > + goto ON_EXIT; > + } > + > + // > + // 2. If the returned StatusCode is not NULL and the value is not 2XX, > indicates any error happen. > + // NOTE: If there is any error message returned from server, it will be > returned in > + // Payload within RedResponse. > + // > + if (*(RedResponse->StatusCode) < HTTP_STATUS_200_OK || \ > + *(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT) > { > + Status = EFI_DEVICE_ERROR; > + } > + > +ON_EXIT: > + if (JsonValue != NULL) { > + RedResponse->Payload = createRedfishPayload (JsonValue, > RedfishService); > + if (RedResponse->Payload == NULL) { > + // > + // Ignore the error when create RedfishPayload, just free the JsonValue > since it's not what > + // we care about if the returned StatusCode is 2XX. > + // > + JsonValueFree (JsonValue); > + } > + } > + > + return Status; > +} > +/** > + Dump text in fractions. > + > + @param[in] String ASCII string to dump. > + > +**/ > +VOID > +RedfishDumpJsonStringFractions ( > + IN CHAR8 *String > + ) > +{ > + CHAR8 *NextFraction; > + UINTN StringFractionSize; > + UINTN StrLen; > + UINTN Count; > + CHAR8 BackupChar; > + > + StringFractionSize = 200; > + if (String == NULL) { > + return ; > + } > + > + DEBUG((DEBUG_INFO, "JSON text:\n")); > + NextFraction = String; > + StrLen = AsciiStrLen (String); > + if (StrLen == 0) { > + return; > + } > + for (Count = 0; Count < (StrLen / StringFractionSize); Count++) { > + BackupChar = *(NextFraction + StringFractionSize); > + *(NextFraction + StringFractionSize) = 0; > + DEBUG((DEBUG_INFO, "%a", NextFraction)); > + *(NextFraction + StringFractionSize) = BackupChar; > + NextFraction += StringFractionSize; > + } > + if ((StrLen % StringFractionSize) != 0) { > + DEBUG((DEBUG_INFO, "%a\n\n", NextFraction)); > + } > +} > +/** > + Dump text in JSON value. > + > + @param[in] JsonValue The Redfish JSON value to dump. > + > +**/ > +VOID > +RedfishDumpJson ( > + IN EDKII_JSON_VALUE JsonValue > + ) > +{ > + CHAR8 *String; > + > + String = JsonDumpString (JsonValue, 0); > + if (String == NULL) { > + return; > + } > + RedfishDumpJsonStringFractions (String); > + FreePool(String); > +} > +/** > + Extract the JSON text content from REDFISH_PAYLOAD and dump to debug > console. > + > + @param[in] Payload The Redfish payload to dump. > + > +**/ > +VOID > +RedfishDumpPayload ( > + IN REDFISH_PAYLOAD Payload > + ) > +{ > + EDKII_JSON_VALUE JsonValue; > + CHAR8 *String; > + > + JsonValue = NULL; > + String = NULL; > + > + if (Payload == NULL) { > + return; > + } > + > + JsonValue = RedfishJsonInPayload (Payload); > + if (JsonValue == NULL) { > + return; > + } > + > + String = JsonDumpString (JsonValue, 0); > + if (String == NULL) { > + return; > + } > + > + RedfishDumpJsonStringFractions (String); > + FreePool(String); > +} > +/** > + This function will cleanup the HTTP header and Redfish payload resources. > + > + @param[in] StatusCode The status code in HTTP response message. > + @param[in] HeaderCount Number of HTTP header structures in > Headers list. > + @param[in] Headers Array containing list of HTTP headers. > + @param[in] Payload The Redfish payload to dump. > + > +**/ > +VOID > +RedfishFreeResponse ( > + IN EFI_HTTP_STATUS_CODE *StatusCode, > + IN UINTN HeaderCount, > + IN EFI_HTTP_HEADER *Headers, > + IN REDFISH_PAYLOAD Payload > + ) > +{ > + if (StatusCode != NULL) { > + FreePool (StatusCode); > + StatusCode = NULL; > + } > + > + if (HeaderCount != 0 && Headers != NULL) { > + HttpFreeHeaderFields(Headers, HeaderCount); > + Headers = NULL; > + } > + > + if (Payload != NULL) { > + RedfishCleanupPayload (Payload); > + Payload = NULL; > + } > +} > +/** > + Check if the "@odata.type" in Payload is valid or not. > + > + @param[in] Payload The Redfish payload to be checked. > + @param[in] OdataTypeName OdataType will be retrived from > mapping list. > + @param[in] OdataTypeMappingList The list of OdataType. > + @param[in] OdataTypeMappingListSize The number of mapping list > + > + @return TRUE if the "@odata.type" in Payload is valid, otherwise FALSE. > + > +**/ > +BOOLEAN > +RedfishIsValidOdataType ( > + IN REDFISH_PAYLOAD Payload, > + IN CONST CHAR8 *OdataTypeName, > + IN REDFISH_ODATA_TYPE_MAPPING *OdataTypeMappingList, > + IN UINTN OdataTypeMappingListSize > + ) > +{ > + UINTN Index; > + EDKII_JSON_VALUE OdataType; > + EDKII_JSON_VALUE JsonValue; > + > + if (Payload == NULL || OdataTypeName == NULL) { > + return FALSE; > + } > + > + JsonValue = RedfishJsonInPayload (Payload); > + if (!JsonValueIsObject (JsonValue)) { > + return FALSE; > + } > + > + OdataType = JsonObjectGetValue (JsonValueGetObject (JsonValue), > "@odata.type"); > + if (!JsonValueIsString (OdataType) || JsonValueGetAsciiString (OdataType) > == NULL) { > + return FALSE; > + } > + > + for (Index = 0; Index < OdataTypeMappingListSize; Index ++) { > + if (AsciiStrCmp (OdataTypeMappingList[Index].OdataTypeName, > OdataTypeName) == 0 && > + AsciiStrCmp (OdataTypeMappingList[Index].OdataType, > JsonValueGetAsciiString (OdataType)) == 0) { > + return TRUE; > + } > + } > + DEBUG ((DEBUG_INFO, "%a: This Odata type is not in the list.\n", > __FUNCTION__)); > + return FALSE; > +} > +/** > + Check if the payload is collection > + > + @param[in] Payload The Redfish payload to be checked. > + > + @return TRUE if the payload is collection. > + > +**/ > +BOOLEAN > +RedfishIsPayloadCollection ( > + IN REDFISH_PAYLOAD Payload > +) > +{ > + return isPayloadCollection (Payload); > +} > +/** > + Get collection size. > + > + @param[in] Payload The Redfish collection payload > + @param[in] CollectionSize Size of this collection > + > + @return EFI_SUCCESS Coolection size is returned in > CollectionSize > + @return EFI_INVALID_PARAMETER The payload is not a collection. > +**/ > +EFI_STATUS > +RedfishGetCollectionSize( > + IN REDFISH_PAYLOAD Payload, > + IN UINTN *CollectionSize > + ) > +{ > + if (Payload == NULL || CollectionSize == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + if (!RedfishIsPayloadCollection(Payload)) { > + return EFI_INVALID_PARAMETER; > + } > + > + *CollectionSize = (UINTN)getCollectionSize(Payload); > + return EFI_SUCCESS; > +} > +/** > + Get Redfish payload of collection member > + > + @param[in] Payload The Redfish collection payload > + @param[in] Index Index of collection member > + > + @return NULL Fail to get collection member. > + @return Non NULL Payload is returned. > +**/ > +REDFISH_PAYLOAD > +RedfishGetPayloadByIndex ( > + IN REDFISH_PAYLOAD Payload, > + IN UINTN Index > +) > +{ > + REDFISH_RESPONSE RedfishResponse; > + REDFISH_PAYLOAD PayloadReturn; > + > + PayloadReturn = (VOID *)getPayloadByIndex (Payload, Index, > &RedfishResponse.StatusCode); > + if(PayloadReturn == NULL || > + (*(RedfishResponse.StatusCode) < HTTP_STATUS_200_OK && > *(RedfishResponse.StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT)){ > + return NULL; > + } > + return PayloadReturn; > +} > +/** > + Check and return Redfish resource of the given Redpath. > + > + @param[in] RedfishService Pointer to REDFISH_SERVICE > + @param[in] Redpath Redpath of the resource. > + @param[in] Response Optional return the resource. > + > + @return EFI_STATUS > +**/ > +EFI_STATUS > +RedfishCheckIfRedpathExist ( > + IN REDFISH_SERVICE RedfishService, > + IN CHAR8 *Redpath, > + IN REDFISH_RESPONSE *Response OPTIONAL > + ) > +{ > + EFI_STATUS Status; > + REDFISH_RESPONSE TempResponse; > + > + if (Redpath == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + Status = RedfishGetByService (RedfishService, Redpath, &TempResponse); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + if (Response == NULL) { > + RedfishFreeResponse( > + TempResponse.StatusCode, > + TempResponse.HeaderCount, > + TempResponse.Headers, > + TempResponse.Payload > + ); > + } else { > + CopyMem ((VOID *)Response, (VOID *)&TempResponse, sizeof > (REDFISH_RESPONSE)); > + } > + return EFI_SUCCESS; > +} > diff --git a/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.c > b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.c > new file mode 100644 > index 00000000000..7077c371548 > --- /dev/null > +++ b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.c > @@ -0,0 +1,201 @@ > +/** @file > + Internal Functions for RedfishLib. > + > + 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 "RedfishMisc.h" > + > +EDKII_REDFISH_CREDENTIAL_PROTOCOL *mCredentialProtocol = NULL; > + > +/** > + This function returns the string of Redfish service version. > + > + @param[in] RedfishService Redfish service instance. > + @param[out] ServiceVersionStr Redfish service string. > + > + @return EFI_STATUS > + > +**/ > +EFI_STATUS > +RedfishGetServiceVersion ( > + IN REDFISH_SERVICE RedfishService, > + OUT CHAR8 **ServiceVersionStr > + ) > +{ > + redfishService *Redfish; > + CHAR8 **KeysArray; > + UINTN KeysNum; > + > + if (RedfishService == NULL || ServiceVersionStr == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + Redfish = (redfishService *)RedfishService; > + if (Redfish->versions == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + KeysArray = JsonObjectGetKeys (Redfish->versions, &KeysNum); > + if (KeysNum == 0 || KeysArray == NULL) { > + return EFI_NOT_FOUND; > + } > + *ServiceVersionStr = *KeysArray; > + return EFI_SUCCESS; > +} > + > +/** > + Creates a REDFISH_SERVICE which can be later used to access the Redfish > resources. > + > + This function will configure REST EX child according to parameters > described in > + Redfish network host interface in SMBIOS type 42 record. The service > enumerator will also > + handle the authentication flow automatically if HTTP basic auth or Redfish > session > + login is configured to use. > + > + @param[in] RedfishConfigServiceInfo Redfish service information the EFI > Redfish > + feature driver communicates with. > + @param[in] AuthMethod None, HTTP basic auth, or Redfish session login. > + @param[in] UserId User Name used for authentication. > + @param[in] Password Password used for authentication. > + > + @return New created Redfish service, or NULL if error happens. > + > +**/ > +REDFISH_SERVICE > +RedfishCreateLibredfishService ( > + IN REDFISH_CONFIG_SERVICE_INFORMATION > *RedfishConfigServiceInfo, > + IN EDKII_REDFISH_AUTH_METHOD AuthMethod, > + IN CHAR8 *UserId, > + IN CHAR8 *Password > + ) > +{ > + > + UINTN Flags; > + enumeratorAuthentication Auth; > + redfishService* Redfish; > + > + Redfish = NULL; > + > + ZeroMem (&Auth, sizeof (Auth)); > + if (AuthMethod == AuthMethodHttpBasic) { > + Auth.authType = REDFISH_AUTH_BASIC; > + } else if (AuthMethod == AuthMethodRedfishSession) { > + Auth.authType = REDFISH_AUTH_SESSION; > + } > + Auth.authCodes.userPass.username = UserId; > + Auth.authCodes.userPass.password = Password; > + > + Flags = REDFISH_FLAG_SERVICE_NO_VERSION_DOC; > + > + if (AuthMethod != AuthMethodNone) { > + Redfish = createServiceEnumerator(RedfishConfigServiceInfo, NULL, > &Auth, (unsigned int ) Flags); > + } else { > + Redfish = createServiceEnumerator(RedfishConfigServiceInfo, NULL, > NULL, (unsigned int) Flags); > + } > + > + // > + // Zero the Password after use. > + // > + if (Password != NULL) { > + ZeroMem (Password, AsciiStrLen(Password)); > + } > + > + return (REDFISH_SERVICE) Redfish; > +} > + > +/** > + Retrieve platform's Redfish authentication information. > + > + This functions returns the Redfish authentication method together with > the user > + Id and password. > + For AuthMethodNone, UserId and Password will point to NULL which > means authentication > + is not required to access the Redfish service. > + For AuthMethodHttpBasic, the UserId and Password could be used for > + HTTP header authentication as defined by RFC7235. For > AuthMethodRedfishSession, > + the UserId and Password could be used for Redfish session login as > defined by > + Redfish API specification (DSP0266). > + > + Callers are responsible for freeing the returned string storage pointed by > UserId > + and Password. > + > + @param[out] AuthMethod Type of Redfish authentication method. > + @param[out] UserId The pointer to store the returned UserId > string. > + @param[out] Password The pointer to store the returned Password > string. > + > + @retval EFI_SUCCESS Get the authentication information > successfully. > + @retval EFI_INVALID_PARAMETER AuthMethod or UserId or Password is > NULL. > + @retval EFI_UNSUPPORTED Unsupported authentication method is > found. > +**/ > +EFI_STATUS > +RedfishGetAuthInfo ( > + OUT EDKII_REDFISH_AUTH_METHOD *AuthMethod, > + OUT CHAR8 **UserId, > + OUT CHAR8 **Password > + ) > +{ > + EFI_STATUS Status; > + > + if (AuthMethod == NULL || UserId == NULL || Password == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + // > + // Locate Redfish Credential Protocol. > + // > + if (mCredentialProtocol == NULL) { > + Status = gBS->LocateProtocol (&gEdkIIRedfishCredentialProtocolGuid, > NULL, (VOID **)&mCredentialProtocol); > + if (EFI_ERROR (Status)) { > + return EFI_UNSUPPORTED; > + } > + } > + > + ASSERT (mCredentialProtocol != NULL); > + > + Status = mCredentialProtocol->GetAuthInfo (mCredentialProtocol, > AuthMethod, UserId, Password); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "RedfishGetAuthInfo: failed to retrieve Redfish > credential - %r\n", Status)); > + return Status; > + } > + > + return Status; > +} > +/** > + This function returns the string of Redfish service version. > + > + @param[in] ServiceVerisonStr The string of Redfish service version. > + @param[in] Url The URL to build Redpath with ID. > + Start with "/", for example "/Registries" > + @param[in] Id ID string > + @param[out] Redpath Pointer to retrive Redpath, caller has to > free > + the memory allocated for this string. > + @return EFI_STATUS > + > +**/ > +EFI_STATUS > +RedfishBuildRedpathUseId ( > + IN CHAR8 *ServiceVerisonStr, > + IN CHAR8 *Url, > + IN CHAR8 *Id, > + OUT CHAR8 **Redpath > + ) > +{ > + UINTN RedpathSize; > + > + if (Redpath == NULL || ServiceVerisonStr == NULL || Url == NULL || Id == > NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + RedpathSize = AsciiStrLen ("/") + > + AsciiStrLen (ServiceVerisonStr) + > + AsciiStrLen (Url) + > + AsciiStrLen ("[Id=]") + > + AsciiStrLen (Id) + 1; > + *Redpath = AllocatePool(RedpathSize); > + if (*Redpath == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + AsciiSPrint (*Redpath, RedpathSize, "/%a%a[Id=%a]", ServiceVerisonStr, > Url, Id); > + return EFI_SUCCESS; > +} > diff --git > a/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/payload.c > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/payload.c > new file mode 100644 > index 00000000000..bd8d143c4e0 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/payload.c > @@ -0,0 +1,732 @@ > +/** @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 = json_get_type (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))) { > + 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: */ > diff --git > a/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/redpath.c > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/redpath.c > new file mode 100644 > index 00000000000..1fb4346c2b6 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/redpath.c > @@ -0,0 +1,192 @@ > +/** @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 <redpath.h> > + > +static char* getVersion(const char* path, char** end); > +static void parseNode(const char* path, redPathNode* node, > redPathNode** end); > + > +static char* getStringTill(const char* string, const char* terminator, char** > retEnd); > + > +redPathNode* parseRedPath(const char* path) > +{ > + redPathNode* node; > + redPathNode* endNode; > + char* curPath; > + char* end; > + > + if(!path || strlen(path) == 0) > + { > + return NULL; > + } > + > + node = (redPathNode*)calloc(1, sizeof(redPathNode)); > + if(!node) > + { > + return NULL; > + } > + if(path[0] == '/') > + { > + node->isRoot = true; > + if(path[1] == 'v') > + { > + node->version = getVersion(path+1, &curPath); > + if(curPath == NULL) > + { > + return node; > + } > + if(curPath[0] == '/') > + { > + curPath++; > + } > + node->next = parseRedPath(curPath); > + } > + else > + { > + node->next = parseRedPath(path+1); > + } > + return node; > + } > + node->isRoot = false; > + curPath = getStringTill(path, "/", &end); > + endNode = node; > + parseNode(curPath, node, &endNode); > + free(curPath); > + if(end != NULL) > + { > + endNode->next = parseRedPath(end+1); > + } > + return node; > +} > + > +void cleanupRedPath(redPathNode* node) > +{ > + if(!node) > + { > + return; > + } > + cleanupRedPath(node->next); > + node->next = NULL; > + if(node->version) > + { > + free(node->version); > + } > + if(node->nodeName) > + { > + free(node->nodeName); > + } > + if(node->op) > + { > + free(node->op); > + } > + if(node->propName) > + { > + free(node->propName); > + } > + if(node->value) > + { > + free(node->value); > + } > + free(node); > +} > + > +static char* getVersion(const char* path, char** end) > +{ > + return getStringTill(path, "/", end); > +} > + > +static void parseNode(const char* path, redPathNode* node, > redPathNode** end) > +{ > + char* indexStart; > + char* index; > + char* indexEnd; > + char* nodeName = getStringTill(path, "[", &indexStart); > + size_t tmpIndex; > + char* opChars; > + > + node->nodeName = nodeName; > + if(indexStart == NULL) > + { > + *end = node; > + return; > + } > + node->next = (redPathNode*)calloc(1, sizeof(redPathNode)); > + if(!node->next) > + { > + return; > + } > + //Skip past [ > + indexStart++; > + *end = node->next; > + index = getStringTill(indexStart, "]", NULL); > + tmpIndex = (size_t)strtoull(index, &indexEnd, 0); > + if(indexEnd != index) > + { > + free(index); > + node->next->index = tmpIndex; > + node->next->isIndex = true; > + return; > + } > + opChars = strpbrk(index, "<>=~"); > + if(opChars == NULL) > + { > + //TODO handle last() and position() > + node->next->op = strdup("exists"); > + node->next->propName = index; > + return; > + } > + node->next->propName = (char*)malloc((opChars - index)+1); > + memcpy(node->next->propName, index, (opChars - index)); > + node->next->propName[(opChars - index)] = 0; > + > + tmpIndex = 1; > + while(1) > + { > + if(opChars[tmpIndex] == '=' || opChars[tmpIndex] == '<' || > opChars[tmpIndex] == '>' || opChars[tmpIndex] == '~') > + { > + tmpIndex++; > + continue; > + } > + break; > + } > + > + node->next->op = (char*)malloc(tmpIndex+1); > + memcpy(node->next->op, opChars, tmpIndex); > + node->next->op[tmpIndex] = 0; > + > + node->next->value = strdup(opChars+tmpIndex); > + free(index); > +} > + > +static char* getStringTill(const char* string, const char* terminator, char** > retEnd) > +{ > + char* ret; > + char* end; > + end = strstr((char*)string, terminator); > + if(retEnd) > + { > + *retEnd = end; > + } > + if(end == NULL) > + { > + //No terminator > + return strdup(string); > + } > + ret = (char*)malloc((end-string)+1); > + memcpy(ret, string, (end-string)); > + ret[(end-string)] = 0; > + return ret; > +} > diff --git > a/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c > new file mode 100644 > index 00000000000..7713f89e6df > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c > @@ -0,0 +1,1396 @@ > +/** @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 <redfishService.h> > +#include <redfishPayload.h> > +#include <redpath.h> > + > +static int initRest(redfishService* service, void * restProtocol); > +static redfishService* createServiceEnumeratorNoAuth(const char* host, > const char* rootUri, bool enumerate, unsigned int flags, void * restProtocol); > +static redfishService* createServiceEnumeratorBasicAuth(const char* host, > const char* rootUri, const char* username, const char* password, unsigned > int flags, void * restProtocol); > +static redfishService* createServiceEnumeratorSessionAuth(const char* > host, const char* rootUri, const char* username, const char* password, > unsigned int flags, void * restProtocol); > +static char* makeUrlForService(redfishService* service, const char* uri); > +static json_t* getVersions(redfishService* service, const char* rootUri); > +static void addStringToJsonObject(json_t* object, const char* key, const > char* value); > + > +CHAR16* > +C8ToC16 (CHAR8 *AsciiStr) > +{ > + CHAR16 *Str; > + UINTN BufLen; > + > + BufLen = (AsciiStrLen (AsciiStr) + 1) * 2; > + Str = AllocatePool (BufLen); > + ASSERT (Str != NULL); > + > + AsciiStrToUnicodeStrS (AsciiStr, Str, AsciiStrLen (AsciiStr) + 1); > + > + return Str; > +} > + > +VOID > +RestConfigFreeHttpRequestData ( > + IN EFI_HTTP_REQUEST_DATA *RequestData > + ) > +{ > + if (RequestData == NULL) { > + return ; > + } > + > + if (RequestData->Url != NULL) { > + FreePool (RequestData->Url); > + } > + > + FreePool (RequestData); > +} > + > +VOID > +RestConfigFreeHttpMessage ( > + IN EFI_HTTP_MESSAGE *Message, > + IN BOOLEAN IsRequest > + ) > +{ > + if (Message == NULL) { > + return ; > + } > + > + if (IsRequest) { > + RestConfigFreeHttpRequestData (Message->Data.Request); > + Message->Data.Request = NULL; > + } else { > + if (Message->Data.Response != NULL) { > + FreePool (Message->Data.Response); > + Message->Data.Response = NULL; > + } > + } > + > + if (Message->Headers != NULL) { > + FreePool (Message->Headers); > + Message->Headers = NULL; > + } > + if (Message->Body != NULL) { > + FreePool (Message->Body); > + Message->Body = NULL; > + } > +} > + > +/** > + Converts the Unicode string to ASCII string to a new allocated buffer. > + > + @param[in] String Unicode string to be converted. > + > + @return Buffer points to ASCII string, or NULL if error happens. > + > +**/ > + > +CHAR8 * > +UnicodeStrDupToAsciiStr ( > + CONST CHAR16 *String > + ) > +{ > + CHAR8 *AsciiStr; > + UINTN BufLen; > + EFI_STATUS Status; > + > + BufLen = StrLen (String) + 1; > + AsciiStr = AllocatePool (BufLen); > + if (AsciiStr == NULL) { > + return NULL; > + } > + > + Status = UnicodeStrToAsciiStrS (String, AsciiStr, BufLen); > + if (EFI_ERROR (Status)) { > + return NULL; > + } > + > + return AsciiStr; > +} > +/** > + This function encodes the content. > + > + @param[in] ContentEncodedValue HTTP conent encoded value. > + @param[in] OriginalContent Original content. > + @param[out] EncodedContent Pointer to receive encoded content. > + @param[out] EncodedContentLength Length of encoded content. > + > + @retval EFI_SUCCESS Content encoded successfully. > + @retval EFI_UNSUPPORTED No source encoding funciton, > + @retval EFI_INVALID_PARAMETER One of the given parameter is invalid. > + > +**/ > +EFI_STATUS > +EncodeRequestContent ( > + IN CHAR8 *ContentEncodedValue, > + IN CHAR8 *OriginalContent, > + OUT VOID **EncodedContent, > + OUT UINTN *EncodedContentLength > +) > +{ > + EFI_STATUS Status; > + VOID *EncodedPointer; > + UINTN EncodedLength; > + > + if (OriginalContent == NULL || EncodedContent == NULL || > EncodedContentLength == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + Status = RedfishContentEncode ( > + ContentEncodedValue, > + OriginalContent, > + AsciiStrLen (OriginalContent), > + &EncodedPointer, > + &EncodedLength > + ); > + if (Status == EFI_SUCCESS) { > + *EncodedContent = EncodedPointer; > + *EncodedContentLength = EncodedLength; > + return EFI_SUCCESS; > + } > + return Status; > +} > + > +/** > + This function decodes the content. The Memory block pointed by > + ContentPointer would be freed and replaced with the cooked Redfish > + payload. > + > + @param[in] ContentEncodedValue HTTP conent encoded value. > + @param[in, out] ContentPointer Pointer to encoded content. > + Pointer of decoded content when out. > + @param[in, out] ContentLength Pointer to the length of encoded > content. > + Length of decoded content when out. > + > + @retval EFI_SUCCESS Functinos found. > + @retval EFI_UNSUPPORTED No functions found. > + @retval EFI_INVALID_PARAMETER One of the given parameter is invalid. > + > +**/ > +EFI_STATUS > +DecodeResponseContent ( > + IN CHAR8 *ContentEncodedValue, > + IN OUT VOID **ContentPointer, > + IN OUT UINTN *ContentLength > +) > +{ > + EFI_STATUS Status; > + VOID *DecodedPointer; > + UINTN DecodedLength; > + > + if (ContentEncodedValue == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + Status = RedfishContentDecode ( > + ContentEncodedValue, > + *ContentPointer, > + *ContentLength, > + &DecodedPointer, > + &DecodedLength > + ); > + if (Status == EFI_SUCCESS) { > + FreePool (*ContentPointer); > + *ContentPointer = DecodedPointer; > + *ContentLength = DecodedLength; > + } > + return Status; > +} > + > +/** > + Create a HTTP URL string for specific Redfish resource. > + > + This function build a URL string from the Redfish Host interface record and > caller specified > + relative path of the resource. > + > + Callers are responsible for freeing the returned string storage pointed by > HttpUrl. > + > + @param[in] RedfishData Redfish network host interface record. > + @param[in] RelativePath Relative path of a resource. > + @param[out] HttpUrl The pointer to store the returned URL > string. > + > + @retval EFI_SUCCESS Build the URL string successfully. > + @retval EFI_INVALID_PARAMETER RedfishData or HttpUrl is NULL. > + @retval EFI_OUT_OF_RESOURCES There are not enough memory > resources. > + > +**/ > +EFI_STATUS > +RedfishBuildUrl ( > + IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo, > + IN CHAR16 *RelativePath, OPTIONAL > + OUT CHAR16 **HttpUrl > + ) > +{ > + CHAR16 *Url; > + CHAR16 *UrlHead; > + UINTN UrlLength; > + UINTN PathLen; > + > + if ((RedfishConfigServiceInfo == NULL) || (HttpUrl == NULL)) { > + return EFI_INVALID_PARAMETER; > + } > + > + // > + // RFC2616: http_URL = "http(s):" "//" host [ ":" port ] [ abs_path [ "?" > query ]] > + // > + if (RelativePath == NULL) { > + PathLen = 0; > + } else { > + PathLen = StrLen (RelativePath); > + } > + UrlLength = StrLen (HTTPS_FLAG) + StrLen (REDFISH_FIRST_URL) + 1 + > StrLen(RedfishConfigServiceInfo->RedfishServiceLocation) + PathLen; > + Url = AllocateZeroPool (UrlLength * sizeof (CHAR16)); > + if (Url == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + UrlHead = Url; > + // > + // Copy "http://" or "https://" according RedfishServiceIpPort. > + // > + if (!RedfishConfigServiceInfo->RedfishServiceUseHttps) { > + StrCpyS (Url, StrLen (HTTPS_FLAG) + 1, HTTP_FLAG); > + Url = Url + StrLen (HTTP_FLAG); > + } else { > + StrCpyS (Url, StrLen (HTTPS_FLAG) + 1, HTTPS_FLAG); > + Url = Url + StrLen (HTTPS_FLAG); > + } > + > + StrCpyS (Url, StrLen (RedfishConfigServiceInfo->RedfishServiceLocation) + > 1, RedfishConfigServiceInfo->RedfishServiceLocation); > + Url = Url + StrLen (RedfishConfigServiceInfo->RedfishServiceLocation); > + > + // > + // Copy abs_path > + // > + if (RelativePath != NULL && PathLen != 0 ) { > + StrnCpyS (Url, UrlLength, RelativePath, PathLen); > + } > + *HttpUrl = UrlHead; > + return EFI_SUCCESS; > +} > + > +redfishService* > createServiceEnumerator(REDFISH_CONFIG_SERVICE_INFORMATION > *RedfishConfigServiceInfo, const char* rootUri, enumeratorAuthentication* > auth, unsigned int flags) > +{ > + EFI_STATUS Status; > + CHAR16 *HttpUrl; > + CHAR8 *AsciiHost; > + EFI_REST_EX_PROTOCOL *RestEx; > + redfishService *ret; > + > + HttpUrl = NULL; > + AsciiHost = NULL; > + RestEx = NULL; > + ret = NULL; > + > + if (RedfishConfigServiceInfo->RedfishServiceRestExHandle == NULL) { > + goto ON_EXIT; > + } > + Status = RedfishBuildUrl(RedfishConfigServiceInfo, NULL, &HttpUrl); > + if (EFI_ERROR (Status)) { > + goto ON_EXIT; > + } > + > + ASSERT (HttpUrl != NULL); > + > + AsciiHost = UnicodeStrDupToAsciiStr (HttpUrl); > + if (AsciiHost == NULL) { > + goto ON_EXIT; > + } > + > + Status = gBS->HandleProtocol ( > + RedfishConfigServiceInfo->RedfishServiceRestExHandle, > + &gEfiRestExProtocolGuid, > + (VOID **)&RestEx > + ); > + if (EFI_ERROR (Status)) { > + goto ON_EXIT; > + } > + if(auth == NULL) { > + ret = createServiceEnumeratorNoAuth(AsciiHost, rootUri, true, flags, > RestEx); > + } else if(auth->authType == REDFISH_AUTH_BASIC) { > + ret = createServiceEnumeratorBasicAuth(AsciiHost, rootUri, auth- > >authCodes.userPass.username, auth->authCodes.userPass.password, flags, > RestEx); > + } else if(auth->authType == REDFISH_AUTH_SESSION) { > + ret = createServiceEnumeratorSessionAuth(AsciiHost, rootUri, auth- > >authCodes.userPass.username, auth->authCodes.userPass.password, flags, > RestEx); > + } else { > + goto ON_EXIT; > + } > + > + ret->RestEx = RestEx; > +ON_EXIT: > + if (HttpUrl != NULL) { > + FreePool (HttpUrl); > + } > + > + if (AsciiHost != NULL) { > + FreePool (AsciiHost); > + } > + > + return ret; > +} > + > +json_t* getUriFromService(redfishService* service, const char* uri, > EFI_HTTP_STATUS_CODE** StatusCode) > +{ > + char* url; > + json_t* ret; > + HTTP_IO_HEADER *HttpIoHeader = NULL; > + EFI_STATUS Status; > + EFI_HTTP_REQUEST_DATA *RequestData = NULL; > + EFI_HTTP_MESSAGE *RequestMsg = NULL; > + EFI_HTTP_MESSAGE ResponseMsg; > + EFI_HTTP_HEADER *ContentEncodedHeader; > + > + if(service == NULL || uri == NULL || StatusCode == NULL) > + { > + return NULL; > + } > + > + *StatusCode = NULL; > + > + url = makeUrlForService(service, uri); > + if(!url) > + { > + return NULL; > + } > + > + DEBUG((DEBUG_INFO, "libredfish: getUriFromService(): %a\n", url)); > + > + // > + // Step 1: Create HTTP request message with 4 headers: > + // > + HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service- > >basicAuthStr) ? 6 : 5); > + if (HttpIoHeader == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + if(service->sessionToken) > + { > + Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service- > >sessionToken); > + ASSERT_EFI_ERROR (Status); > + } else if (service->basicAuthStr) { > + Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service- > >basicAuthStr); > + ASSERT_EFI_ERROR (Status); > + } > + > + Status = HttpIoSetHeader (HttpIoHeader, "Host", service- > >HostHeaderValue); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Accept", "application/json"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); > + ASSERT_EFI_ERROR (Status); > + > + // > + // Step 2: build the rest of HTTP request info. > + // > + RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); > + if (RequestData == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + RequestData->Method = HttpMethodGet; > + RequestData->Url = C8ToC16 (url); > + > + // > + // Step 3: fill in EFI_HTTP_MESSAGE > + // > + RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); > + if (RequestMsg == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + RequestMsg->Data.Request = RequestData; > + RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; > + RequestMsg->Headers = HttpIoHeader->Headers; > + > + ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); > + > + // > + // Step 4: call RESTEx to get response from REST service. > + // > + Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, > &ResponseMsg); > + if (EFI_ERROR (Status)) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + // > + // Step 5: Return the HTTP StatusCode and Body message. > + // > + if (ResponseMsg.Data.Response != NULL) { > + *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); > + if (*StatusCode == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + // > + // The caller shall take the responsibility to free the buffer. > + // > + **StatusCode = ResponseMsg.Data.Response->StatusCode; > + } > + > + if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { > + // > + // Check if data is encoded. > + // > + ContentEncodedHeader = HttpFindHeader (ResponseMsg.HeaderCount, > ResponseMsg.Headers, HTTP_HEADER_CONTENT_ENCODING); > + if (ContentEncodedHeader != NULL) { > + // > + // The content is encoded. > + // > + Status = DecodeResponseContent (ContentEncodedHeader->FieldValue, > &ResponseMsg.Body, &ResponseMsg.BodyLength); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response > content %r\n.", __FUNCTION__, Status)); > + ret = NULL; > + goto ON_EXIT; > + } > + } > + ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, > NULL); > + } else { > + // > + // There is no message body returned from server. > + // > + ret = NULL; > + } > + > +ON_EXIT: > + if (url != NULL) { > + free (url); > + } > + > + if (HttpIoHeader != NULL) { > + HttpIoFreeHeader (HttpIoHeader); > + } > + > + if (RequestData != NULL) { > + RestConfigFreeHttpRequestData (RequestData); > + } > + > + if (RequestMsg != NULL) { > + FreePool (RequestMsg); > + } > + > + RestConfigFreeHttpMessage (&ResponseMsg, FALSE); > + > + return ret; > +} > + > +json_t* patchUriFromService(redfishService* service, const char* uri, const > char* content, EFI_HTTP_STATUS_CODE** StatusCode) > +{ > + char* url; > + json_t* ret; > + HTTP_IO_HEADER *HttpIoHeader = NULL; > + EFI_STATUS Status; > + EFI_HTTP_REQUEST_DATA *RequestData = NULL; > + EFI_HTTP_MESSAGE *RequestMsg = NULL; > + EFI_HTTP_MESSAGE ResponseMsg; > + CHAR8 ContentLengthStr[80]; > + CHAR8 *EncodedContent; > + UINTN EncodedContentLen; > + > + if(service == NULL || uri == NULL || content == NULL || StatusCode == > NULL) > + { > + return NULL; > + } > + > + *StatusCode = NULL; > + > + url = makeUrlForService(service, uri); > + if(!url) > + { > + return NULL; > + } > + > + DEBUG((DEBUG_INFO, "libredfish: patchUriFromService(): %a\n", url)); > + > + // > + // Step 1: Create HTTP request message with 4 headers: > + // > + HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service- > >basicAuthStr) ? 9 : 8); > + if (HttpIoHeader == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + if(service->sessionToken) > + { > + Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service- > >sessionToken); > + ASSERT_EFI_ERROR (Status); > + } else if (service->basicAuthStr) { > + Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service- > >basicAuthStr); > + ASSERT_EFI_ERROR (Status); > + } > + > + Status = HttpIoSetHeader (HttpIoHeader, "Host", service- > >HostHeaderValue); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", > "application/json"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Accept", "application/json"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); > + ASSERT_EFI_ERROR (Status); > + > + AsciiSPrint( > + ContentLengthStr, > + sizeof (ContentLengthStr), > + "%lu", > + (UINT64) strlen(content) > + ); > + Status = HttpIoSetHeader (HttpIoHeader, "Content-Length", > ContentLengthStr); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); > + ASSERT_EFI_ERROR (Status); > + > + // > + // Step 2: build the rest of HTTP request info. > + // > + RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); > + if (RequestData == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + RequestData->Method = HttpMethodPatch; > + RequestData->Url = C8ToC16 (url); > + > + // > + // Step 3: fill in EFI_HTTP_MESSAGE > + // > + RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); > + if (RequestMsg == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + EncodedContent = (CHAR8 *)content; > + EncodedContentLen = strlen(content); > + // > + // We currently only support gzip Content-Encoding. > + // > + Status = EncodeRequestContent ((CHAR8 > *)HTTP_CONTENT_ENCODING_GZIP, (CHAR8 *)content, (VOID > **)&EncodedContent, &EncodedContentLen); > + if (Status == EFI_INVALID_PARAMETER) { > + DEBUG((DEBUG_ERROR, "%a: Error to encode content.\n", > __FUNCTION__)); > + ret = NULL; > + goto ON_EXIT; > + } else if (Status == EFI_UNSUPPORTED) { > + DEBUG((DEBUG_INFO, "No content coding for %a! Use raw data > instead.\n", HTTP_CONTENT_ENCODING_GZIP)); > + Status = HttpIoSetHeader (HttpIoHeader, "Content-Encoding", > HTTP_CONTENT_ENCODING_IDENTITY); > + ASSERT_EFI_ERROR (Status); > + } else { > + Status = HttpIoSetHeader (HttpIoHeader, "Content-Encoding", > HTTP_CONTENT_ENCODING_GZIP); > + ASSERT_EFI_ERROR (Status); > + } > + > + RequestMsg->Data.Request = RequestData; > + RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; > + RequestMsg->Headers = HttpIoHeader->Headers; > + RequestMsg->BodyLength = EncodedContentLen; > + RequestMsg->Body = (VOID*) EncodedContent; > + > + ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); > + > + // > + // Step 4: call RESTEx to get response from REST service. > + // > + Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, > &ResponseMsg); > + if (EFI_ERROR (Status)) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + // > + // Step 5: Return the HTTP StatusCode and Body message. > + // > + if (ResponseMsg.Data.Response != NULL) { > + *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); > + if (*StatusCode == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + // > + // The caller shall take the responsibility to free the buffer. > + // > + **StatusCode = ResponseMsg.Data.Response->StatusCode; > + } > + > + if (EncodedContent != content) { > + FreePool (EncodedContent); > + } > + > + > + if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { > + ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, > NULL); > + } else { > + // > + // There is no message body returned from server. > + // > + ret = NULL; > + } > + > +ON_EXIT: > + if (url != NULL) { > + free (url); > + } > + > + if (HttpIoHeader != NULL) { > + HttpIoFreeHeader (HttpIoHeader); > + } > + > + if (RequestData != NULL) { > + RestConfigFreeHttpRequestData (RequestData); > + } > + > + if (RequestMsg != NULL) { > + FreePool (RequestMsg); > + } > + > + RestConfigFreeHttpMessage (&ResponseMsg, FALSE); > + > + return ret; > +} > + > +json_t* postUriFromService(redfishService* service, const char* uri, const > char* content, size_t contentLength, const char* contentType, > EFI_HTTP_STATUS_CODE** StatusCode) > +{ > + char* url = NULL; > + json_t* ret; > + HTTP_IO_HEADER *HttpIoHeader = NULL; > + EFI_STATUS Status; > + EFI_HTTP_REQUEST_DATA *RequestData = NULL; > + EFI_HTTP_MESSAGE *RequestMsg = NULL; > + EFI_HTTP_MESSAGE ResponseMsg; > + CHAR8 ContentLengthStr[80]; > + EFI_HTTP_HEADER *HttpHeader = NULL; > + > + ret = NULL; > + > + if(service == NULL || uri == NULL || content == NULL || StatusCode == > NULL) > + { > + return NULL; > + } > + > + *StatusCode = NULL; > + > + url = makeUrlForService(service, uri); > + if(!url) > + { > + return NULL; > + } > + > + DEBUG((DEBUG_INFO, "libredfish: postUriFromService(): %a\n", url)); > + > + if(contentLength == 0) > + { > + contentLength = strlen(content); > + } > + > + // > + // Step 1: Create HTTP request message with 4 headers: > + // > + HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service- > >basicAuthStr) ? 8 : 7); > + if (HttpIoHeader == NULL) { > + goto ON_EXIT; > + } > + > + if(service->sessionToken) > + { > + Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service- > >sessionToken); > + ASSERT_EFI_ERROR (Status); > + } else if (service->basicAuthStr) { > + Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service- > >basicAuthStr); > + ASSERT_EFI_ERROR (Status); > + } > + > + if(contentType == NULL) { > + Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", > "application/json"); > + ASSERT_EFI_ERROR (Status); > + } else { > + Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", (CHAR8 *) > contentType); > + ASSERT_EFI_ERROR (Status); > + } > + Status = HttpIoSetHeader (HttpIoHeader, "Host", service- > >HostHeaderValue); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Accept", "application/json"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); > + ASSERT_EFI_ERROR (Status); > + AsciiSPrint( > + ContentLengthStr, > + sizeof (ContentLengthStr), > + "%lu", > + (UINT64) contentLength > + ); > + Status = HttpIoSetHeader (HttpIoHeader, "Content-Length", > ContentLengthStr); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); > + ASSERT_EFI_ERROR (Status); > + > + // > + // Step 2: build the rest of HTTP request info. > + // > + RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); > + if (RequestData == NULL) { > + goto ON_EXIT; > + } > + > + RequestData->Method = HttpMethodPost; > + RequestData->Url = C8ToC16 (url); > + > + // > + // Step 3: fill in EFI_HTTP_MESSAGE > + // > + RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); > + if (RequestMsg == NULL) { > + goto ON_EXIT; > + } > + > + RequestMsg->Data.Request = RequestData; > + RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; > + RequestMsg->Headers = HttpIoHeader->Headers; > + RequestMsg->BodyLength = contentLength; > + RequestMsg->Body = (VOID*) content; > + > + ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); > + > + // > + // Step 4: call RESTEx to get response from REST service. > + // > + Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, > &ResponseMsg); > + if (EFI_ERROR (Status)) { > + goto ON_EXIT; > + } > + > + // > + // Step 5: Return the HTTP StatusCode and Body message. > + // > + if (ResponseMsg.Data.Response != NULL) { > + *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); > + if (*StatusCode == NULL) { > + goto ON_EXIT; > + } > + > + // > + // The caller shall take the responsibility to free the buffer. > + // > + **StatusCode = ResponseMsg.Data.Response->StatusCode; > + } > + > + if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { > + ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, > NULL); > + } > + > + // > + // Step 6: Parsing the HttpHeader to retrive the X-Auth-Token if the HTTP > StatusCode is correct. > + // > + if (ResponseMsg.Data.Response->StatusCode == HTTP_STATUS_200_OK > || > + ResponseMsg.Data.Response->StatusCode == > HTTP_STATUS_204_NO_CONTENT) { > + HttpHeader = HttpFindHeader (ResponseMsg.HeaderCount, > ResponseMsg.Headers, "X-Auth-Token"); > + if (HttpHeader != NULL) { > + if(service->sessionToken) > + { > + free(service->sessionToken); > + } > + service->sessionToken = AllocateCopyPool (AsciiStrSize (HttpHeader- > >FieldValue), HttpHeader->FieldValue); > + } > + > + /* > + // > + // Below opeation seems to be unnecessary. > + // Besides, the FieldValue for the Location is the full HTTP URI > (Http://0.0.0.0:5000/XXX > iHOsr8SLyeerAbEcgArqMRZeev30bDQRCkl9tuo3i68lbpuzRSqgWU$ ), so we > can't use it as the > + // parameter of getUriFromService () directly. > + // > + HttpHeader = HttpFindHeader (ResponseMsg.HeaderCount, > ResponseMsg.Headers, "Location"); > + if (HttpHeader != NULL) { > + ret = getUriFromService(service, HttpHeader->FieldValue); > + goto ON_EXIT; > + } > + */ > + } > + > +ON_EXIT: > + if (url != NULL) { > + free (url); > + } > + > + if (HttpIoHeader != NULL) { > + HttpIoFreeHeader (HttpIoHeader); > + } > + > + if (RequestData != NULL) { > + RestConfigFreeHttpRequestData (RequestData); > + } > + > + if (RequestMsg != NULL) { > + FreePool (RequestMsg); > + } > + > + RestConfigFreeHttpMessage (&ResponseMsg, FALSE); > + > + return ret; > +} > + > +json_t* deleteUriFromService(redfishService* service, const char* uri, > EFI_HTTP_STATUS_CODE** StatusCode) > +{ > + char* url; > + json_t* ret; > + HTTP_IO_HEADER *HttpIoHeader = NULL; > + EFI_STATUS Status; > + EFI_HTTP_REQUEST_DATA *RequestData = NULL; > + EFI_HTTP_MESSAGE *RequestMsg = NULL; > + EFI_HTTP_MESSAGE ResponseMsg; > + > + ret = NULL; > + > + if(service == NULL || uri == NULL || StatusCode == NULL) > + { > + return NULL; > + } > + > + *StatusCode = NULL; > + > + url = makeUrlForService(service, uri); > + if(!url) > + { > + return NULL; > + } > + > + DEBUG((DEBUG_INFO, "libredfish: deleteUriFromService(): %a\n", url)); > + > + // > + // Step 1: Create HTTP request message with 4 headers: > + // > + HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service- > >basicAuthStr) ? 5 : 4); > + if (HttpIoHeader == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + if(service->sessionToken) > + { > + Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service- > >sessionToken); > + ASSERT_EFI_ERROR (Status); > + } else if (service->basicAuthStr) { > + Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service- > >basicAuthStr); > + ASSERT_EFI_ERROR (Status); > + } > + Status = HttpIoSetHeader (HttpIoHeader, "Host", service- > >HostHeaderValue); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); > + ASSERT_EFI_ERROR (Status); > + Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); > + ASSERT_EFI_ERROR (Status); > + > + // > + // Step 2: build the rest of HTTP request info. > + // > + RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); > + if (RequestData == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + RequestData->Method = HttpMethodDelete; > + RequestData->Url = C8ToC16 (url); > + > + // > + // Step 3: fill in EFI_HTTP_MESSAGE > + // > + RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); > + if (RequestMsg == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + RequestMsg->Data.Request = RequestData; > + RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; > + RequestMsg->Headers = HttpIoHeader->Headers; > + > + ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); > + > + // > + // Step 4: call RESTEx to get response from REST service. > + // > + Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, > &ResponseMsg); > + if (EFI_ERROR (Status)) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + // > + // Step 5: Return the HTTP StatusCode and Body message. > + // > + if (ResponseMsg.Data.Response != NULL) { > + *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); > + if (*StatusCode == NULL) { > + ret = NULL; > + goto ON_EXIT; > + } > + > + // > + // The caller shall take the responsibility to free the buffer. > + // > + **StatusCode = ResponseMsg.Data.Response->StatusCode; > + } > + > + if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { > + ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, > NULL); > + } > + > +ON_EXIT: > + if (url != NULL) { > + free (url); > + } > + > + if (HttpIoHeader != NULL) { > + HttpIoFreeHeader (HttpIoHeader); > + } > + > + if (RequestData != NULL) { > + RestConfigFreeHttpRequestData (RequestData); > + } > + > + if (RequestMsg != NULL) { > + FreePool (RequestMsg); > + } > + > + RestConfigFreeHttpMessage (&ResponseMsg, FALSE); > + > + return ret; > +} > + > +redfishPayload* getRedfishServiceRoot(redfishService* service, const > char* version, EFI_HTTP_STATUS_CODE** StatusCode) > +{ > + json_t* value; > + json_t* versionNode; > + const char* verUrl; > + > + if(version == NULL) > + { > + versionNode = json_object_get(service->versions, "v1"); > + } > + else > + { > + versionNode = json_object_get(service->versions, version); > + } > + if(versionNode == NULL) > + { > + return NULL; > + } > + verUrl = json_string_value(versionNode); > + if(verUrl == NULL) > + { > + return NULL; > + } > + value = getUriFromService(service, verUrl, StatusCode); > + if(value == NULL) > + { > + if((service->flags & REDFISH_FLAG_SERVICE_NO_VERSION_DOC) == 0) > + { > + json_decref(versionNode); > + } > + return NULL; > + } > + return createRedfishPayload(value, service); > +} > + > +redfishPayload* getPayloadByPath(redfishService* service, const char* > path, EFI_HTTP_STATUS_CODE** StatusCode) > +{ > + redPathNode* redpath; > + redfishPayload* root; > + redfishPayload* ret; > + > + if(!service || !path || StatusCode == NULL) > + { > + return NULL; > + } > + > + *StatusCode = NULL; > + > + redpath = parseRedPath(path); > + if(!redpath) > + { > + return NULL; > + } > + if(!redpath->isRoot) > + { > + cleanupRedPath(redpath); > + return NULL; > + } > + root = getRedfishServiceRoot(service, redpath->version, StatusCode); > + if (*StatusCode == NULL || **StatusCode < HTTP_STATUS_200_OK || > **StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT) { > + cleanupRedPath(redpath); > + return root; > + } > + > + if(redpath->next == NULL) > + { > + cleanupRedPath(redpath); > + return root; > + } > + > + FreePool (*StatusCode); > + *StatusCode = NULL; > + > + ret = getPayloadForPath(root, redpath->next, StatusCode); > + if (*StatusCode == NULL && ret != NULL) { > + // > + // In such a case, the Redfish resource is parsed from the input > payload > (root) directly. > + // So, 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(root); > + cleanupRedPath(redpath); > + return ret; > +} > + > +void cleanupServiceEnumerator(redfishService* service) > +{ > + if(!service) > + { > + return; > + } > + free(service->host); > + json_decref(service->versions); > + if(service->sessionToken != NULL) > + { > + ZeroMem (service->sessionToken, (UINTN)strlen(service- > >sessionToken)); > + FreePool(service->sessionToken); > + } > + if (service->basicAuthStr != NULL) { > + ZeroMem (service->basicAuthStr, (UINTN)strlen(service->basicAuthStr)); > + FreePool (service->basicAuthStr); > + } > + free(service); > +} > + > +static int initRest(redfishService* service, void * restProtocol) > +{ > + service->RestEx = restProtocol; > + return 0; > +} > + > +static redfishService* createServiceEnumeratorNoAuth(const char* host, > const char* rootUri, bool enumerate, unsigned int flags, void * restProtocol) > +{ > + redfishService* ret; > + char *HostStart; > + > + ret = (redfishService*)calloc(1, sizeof(redfishService)); > + ZeroMem (ret, sizeof(redfishService)); > + if(initRest(ret, restProtocol) != 0) > + { > + free(ret); > + return NULL; > + } > + ret->host = AllocateCopyPool(AsciiStrSize(host), host); > + ret->flags = flags; > + if(enumerate) > + { > + ret->versions = getVersions(ret, rootUri); > + } > + HostStart = strstr (ret->host, "//"); > + if (HostStart != NULL && (*(HostStart + 2) != '\0')) { > + ret->HostHeaderValue = HostStart + 2; > + } > + > + return ret; > +} > + > +EFI_STATUS > +createBasicAuthStr ( > + IN redfishService* service, > + IN CONST CHAR8 *UserId, > + IN CONST CHAR8 *Password > + ) > +{ > + EFI_STATUS Status; > + CHAR8 *RawAuthValue; > + UINTN RawAuthBufSize; > + CHAR8 *EnAuthValue; > + UINTN EnAuthValueSize; > + CHAR8 *BasicWithEnAuthValue; > + UINTN BasicBufSize; > + > + EnAuthValue = NULL; > + EnAuthValueSize = 0; > + > + RawAuthBufSize = AsciiStrLen (UserId) + AsciiStrLen (Password) + 2; > + RawAuthValue = AllocatePool (RawAuthBufSize); > + if (RawAuthValue == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + // > + // Build raw AuthValue (UserId:Password). > + // > + AsciiSPrint ( > + RawAuthValue, > + RawAuthBufSize, > + "%a:%a", > + UserId, > + Password > + ); > + > + // > + // Encoding RawAuthValue into Base64 format. > + // > + Status = Base64Encode ( > + (CONST UINT8 *) RawAuthValue, > + AsciiStrLen (RawAuthValue), > + EnAuthValue, > + &EnAuthValueSize > + ); > + if (Status == EFI_BUFFER_TOO_SMALL) { > + EnAuthValue = (CHAR8 *) AllocateZeroPool (EnAuthValueSize); > + if (EnAuthValue == NULL) { > + Status = EFI_OUT_OF_RESOURCES; > + return Status; > + } > + > + Status = Base64Encode ( > + (CONST UINT8 *) RawAuthValue, > + AsciiStrLen (RawAuthValue), > + EnAuthValue, > + &EnAuthValueSize > + ); > + } > + > + if (EFI_ERROR (Status)) { > + goto Exit; > + } > + > + BasicBufSize = AsciiStrLen ("Basic ") + AsciiStrLen(EnAuthValue) + 2; > + BasicWithEnAuthValue = AllocatePool (BasicBufSize); > + if (BasicWithEnAuthValue == NULL) { > + Status = EFI_OUT_OF_RESOURCES; > + goto Exit; > + } > + > + // > + // Build encoded EnAuthValue with Basic (Basic EnAuthValue). > + // > + AsciiSPrint ( > + BasicWithEnAuthValue, > + BasicBufSize, > + "%a %a", > + "Basic", > + EnAuthValue > + ); > + > + service->basicAuthStr = BasicWithEnAuthValue; > + > +Exit: > + if (RawAuthValue != NULL) { > + ZeroMem (RawAuthValue, RawAuthBufSize); > + FreePool (RawAuthValue); > + } > + > + if (EnAuthValue != NULL) { > + ZeroMem (EnAuthValue, EnAuthValueSize); > + FreePool (EnAuthValue); > + } > + > + return Status; > +} > + > +static redfishService* createServiceEnumeratorBasicAuth(const char* host, > const char* rootUri, const char* username, const char* password, unsigned > int flags, void * restProtocol) > +{ > + redfishService* ret; > + EFI_STATUS Status; > + > + ret = createServiceEnumeratorNoAuth(host, rootUri, false, flags, > restProtocol); > + > + // add basic auth str > + Status = createBasicAuthStr (ret, username, password); > + if (EFI_ERROR(Status)) { > + cleanupServiceEnumerator (ret); > + return NULL; > + } > + > + ret->versions = getVersions(ret, rootUri); > + return ret; > +} > + > +static redfishService* createServiceEnumeratorSessionAuth(const char* > host, const char* rootUri, const char* username, const char* password, > unsigned int flags, void * restProtocol) > +{ > + redfishService* ret; > + redfishPayload* payload; > + redfishPayload* links; > + json_t* sessionPayload; > + json_t* session; > + json_t* odataId; > + const char* uri; > + json_t* post; > + char* content; > + EFI_HTTP_STATUS_CODE *StatusCode; > + > + content = NULL; > + StatusCode = NULL; > + > + ret = createServiceEnumeratorNoAuth(host, rootUri, true, flags, > restProtocol); > + if(ret == NULL) > + { > + return NULL; > + } > + payload = getRedfishServiceRoot(ret, NULL, &StatusCode); > + if(StatusCode == NULL || *StatusCode < HTTP_STATUS_200_OK || > *StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT) > + { > + if (StatusCode != NULL) { > + FreePool (StatusCode); > + } > + > + if (payload != NULL) { > + cleanupPayload(payload); > + } > + cleanupServiceEnumerator(ret); > + return NULL; > + } > + > + if (StatusCode != NULL) { > + FreePool (StatusCode); > + StatusCode = NULL; > + } > + > + links = getPayloadByNodeName(payload, "Links", &StatusCode); > + cleanupPayload(payload); > + if(links == NULL) > + { > + cleanupServiceEnumerator(ret); > + return NULL; > + } > + session = json_object_get(links->json, "Sessions"); > + if(session == NULL) > + { > + cleanupPayload(links); > + cleanupServiceEnumerator(ret); > + return NULL; > + } > + odataId = json_object_get(session, "@odata.id"); > + if(odataId == NULL) > + { > + cleanupPayload(links); > + cleanupServiceEnumerator(ret); > + return NULL; > + } > + uri = json_string_value(odataId); > + post = json_object(); > + addStringToJsonObject(post, "UserName", username); > + addStringToJsonObject(post, "Password", password); > + content = json_dumps(post, 0); > + json_decref(post); > + sessionPayload = postUriFromService(ret, uri, content, 0, NULL, > &StatusCode); > + > + if (content != NULL) { > + ZeroMem (content, (UINTN)strlen(content)); > + free(content); > + } > + > + if(sessionPayload == NULL || StatusCode == NULL || *StatusCode < > HTTP_STATUS_200_OK || *StatusCode > > HTTP_STATUS_206_PARTIAL_CONTENT) > + { > + //Failed to create session! > + > + cleanupPayload(links); > + cleanupServiceEnumerator(ret); > + > + if (StatusCode != NULL) { > + FreePool (StatusCode); > + } > + > + if (sessionPayload != NULL) { > + json_decref(sessionPayload); > + } > + > + return NULL; > + } > + json_decref(sessionPayload); > + cleanupPayload(links); > + FreePool (StatusCode); > + return ret; > +} > + > +static char* makeUrlForService(redfishService* service, const char* uri) > +{ > + char* url; > + if(service->host == NULL) > + { > + return NULL; > + } > + url = (char*)malloc(strlen(service->host)+strlen(uri)+1); > + strcpy(url, service->host); > + strcat(url, uri); > + return url; > +} > + > +static json_t* getVersions(redfishService* service, const char* rootUri) > +{ > + json_t* ret = NULL; > + EFI_HTTP_STATUS_CODE* StatusCode = NULL; > + > + if(service->flags & REDFISH_FLAG_SERVICE_NO_VERSION_DOC) > + { > + service->versions = json_object(); > + if(service->versions == NULL) > + { > + return NULL; > + } > + addStringToJsonObject(service->versions, "v1", "/redfish/v1"); > + return service->versions; > + } > + if(rootUri != NULL) > + { > + ret = getUriFromService(service, rootUri, &StatusCode); > + } > + else > + { > + ret = getUriFromService(service, "/redfish", &StatusCode); > + } > + > + if (ret == NULL || StatusCode == NULL || *StatusCode < > HTTP_STATUS_200_OK || *StatusCode > > HTTP_STATUS_206_PARTIAL_CONTENT) { > + if (ret != NULL) { > + json_decref(ret); > + } > + ret = NULL; > + } > + > + if (StatusCode != NULL) { > + FreePool (StatusCode); > + } > + > + return ret; > +} > + > +static void addStringToJsonObject(json_t* object, const char* key, const > char* value) > +{ > + json_t* jValue = json_string(value); > + > + json_object_set(object, key, jValue); > + > + json_decref(jValue); > +} > -- > 2.21.0.windows.1 > > > > > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#82784): https://edk2.groups.io/g/devel/message/82784 Mute This Topic: https://groups.io/mt/86643869/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-