[AMD Official Use Only - General] Reviewed-by: Abner Chang <abner.ch...@amd.com>
> -----Original Message----- > From: Nickle Wang <nick...@nvidia.com> > Sent: Thursday, May 4, 2023 10:28 PM > To: devel@edk2.groups.io > Cc: Chang, Abner <abner.ch...@amd.com>; Igor Kulchytskyy > <ig...@ami.com> > Subject: [edk2-redfish-client][PATCH 7/8] RedfishClientPkg: RedfishLib > > Caution: This message originated from an External Source. Use proper > caution when opening attachments, clicking links, or responding. > > > (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) > > 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 <nick...@nvidia.com> > Cc: Abner Chang <abner.ch...@amd.com> > Cc: Igor Kulchytskyy <ig...@ami.com> > --- > RedfishClientPkg/RedfishClientPkg.dec | 5 + > RedfishClientPkg/RedfishClientLibs.dsc.inc | 4 + > RedfishClientPkg/RedfishClientPkg.dsc | 1 + > .../PrivateLibrary/RedfishLib/RedfishLib.inf | 62 + > .../PrivateLibrary/RedfishLib/RedfishMisc.h | 82 + > .../edk2libredfish/include/redfish.h | 25 + > .../edk2libredfish/include/redfishPayload.h | 106 ++ > .../edk2libredfish/include/redfishService.h | 151 ++ > .../edk2libredfish/include/redpath.h | 49 + > .../PrivateLibrary/RedfishLib/RedfishLib.c | 1033 +++++++++++ > .../PrivateLibrary/RedfishLib/RedfishMisc.c | 206 +++ > .../RedfishLib/edk2libredfish/src/payload.c | 812 +++++++++ > .../RedfishLib/edk2libredfish/src/redpath.c | 224 +++ > .../RedfishLib/edk2libredfish/src/service.c | 1523 +++++++++++++++++ > 14 files changed, 4283 insertions(+) > 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 b965f915..09df062d 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 a5ae73ca..4655dd70 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 00a963ea..2b2149cc 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 > diff --git a/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf > new file mode 100644 > index 00000000..a54e397d > --- /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 00000000..3b066c11 > --- /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 00000000..e9b9b3fa > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > .h > @@ -0,0 +1,25 @@ > +/** @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 00000000..44515306 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Payload.h > @@ -0,0 +1,106 @@ > +/** @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 00000000..5c13b682 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfish > Service.h > @@ -0,0 +1,151 @@ > +/** @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 00000000..c1a1568b > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redpat > h.h > @@ -0,0 +1,49 @@ > +/** @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 00000000..9f9d3779 > --- /dev/null > +++ b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.c > @@ -0,0 +1,1033 @@ > +/** @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 00000000..0eb23196 > --- /dev/null > +++ b/RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishMisc.c > @@ -0,0 +1,206 @@ > +/** @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 00000000..7ceb63ac > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/payload.c > @@ -0,0 +1,812 @@ > +/** @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 00000000..cf5ab851 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/redpath.c > @@ -0,0 +1,224 @@ > +/** @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 00000000..969aa5a0 > --- /dev/null > +++ > b/RedfishClientPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c > @@ -0,0 +1,1523 @@ > +/** @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), 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.17.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#104061): https://edk2.groups.io/g/devel/message/104061 Mute This Topic: https://groups.io/mt/98684898/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-