/** @file This file is cloned from DMTF libredfish library tag v1.0.0 and maintained by EDKII. //---------------------------------------------------------------------------- // Copyright Notice: // Copyright 2017 Distributed Management Task Force, Inc. All rights reserved. // License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libredfish/LICENSE.md //---------------------------------------------------------------------------- Copyright (c) 2019, Intel Corporation. All rights reserved.
(C) Copyright 2021 Hewlett Packard Enterprise Development LP
Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include 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; } EFI_HTTP_HEADER * cloneHttpHeaders ( EFI_HTTP_MESSAGE *message, UINTN *HeaderCount ) { EFI_HTTP_HEADER *Buffer; UINTN Index; if ((message == NULL) || (HeaderCount == NULL)) { return NULL; } *HeaderCount = message->HeaderCount; Buffer = AllocatePool (sizeof (EFI_HTTP_HEADER) * message->HeaderCount); if (Buffer == NULL) { return NULL; } for (Index = 0; Index < message->HeaderCount; Index++) { Buffer[Index].FieldName = AllocateCopyPool (AsciiStrSize (message->Headers[Index].FieldName), message->Headers[Index].FieldName); ASSERT (Buffer[Index].FieldName != NULL); Buffer[Index].FieldValue = AllocateCopyPool (AsciiStrSize (message->Headers[Index].FieldValue), message->Headers[Index].FieldValue); ASSERT (Buffer[Index].FieldValue != NULL); } return Buffer; } json_t * getUriFromServiceEx ( redfishService *service, const char *uri, EFI_HTTP_HEADER **Headers OPTIONAL, UINTN *HeaderCount OPTIONAL, 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; if (HeaderCount != NULL) { *HeaderCount = 0; } if (Headers != NULL) { *Headers = NULL; } url = makeUrlForService (service, uri); if (!url) { return NULL; } DEBUG ((DEBUG_MANAGEABILITY, "%a: %a\n", __func__, 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; // // Deliver status code to caller when error happens so caller can do error handling. // 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; } 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.Headers != NULL) && (Headers != NULL) && (HeaderCount != NULL)) { *Headers = cloneHttpHeaders (&ResponseMsg, HeaderCount); } 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.", __func__, 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 * putUriFromServiceEx ( redfishService *service, const char *uri, const char *content, size_t contentLength, const char *contentType, EFI_HTTP_HEADER **Headers OPTIONAL, UINTN *HeaderCount OPTIONAL, 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; if (HeaderCount != NULL) { *HeaderCount = 0; } if (Headers != NULL) { *Headers = NULL; } url = makeUrlForService (service, uri); if (url == NULL) { return NULL; } DEBUG ((DEBUG_MANAGEABILITY, "%a: %a\n", __func__, url)); if (contentLength == 0) { contentLength = strlen (content); } // // Step 1: Create HTTP request message with 4 headers: // HttpIoHeader = HttpIoCreateHeader ((service->sessionToken != NULL || service->basicAuthStr != NULL) ? 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); } 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, "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)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) { ret = NULL; goto ON_EXIT; } RequestData->Method = HttpMethodPut; 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 = contentLength; // // 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", __func__)); ret = NULL; goto ON_EXIT; } else if (Status == EFI_UNSUPPORTED) { DEBUG ((DEBUG_MANAGEABILITY, "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 ((ResponseMsg.Headers != NULL) && (Headers != NULL) && (HeaderCount != NULL)) { *Headers = cloneHttpHeaders (&ResponseMsg, HeaderCount); } 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 * patchUriFromServiceEx ( redfishService *service, const char *uri, const char *content, EFI_HTTP_HEADER **Headers OPTIONAL, UINTN *HeaderCount OPTIONAL, 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; if (HeaderCount != NULL) { *HeaderCount = 0; } if (Headers != NULL) { *Headers = NULL; } url = makeUrlForService (service, uri); if (!url) { return NULL; } DEBUG ((DEBUG_MANAGEABILITY, "%a: %a\n", __func__, 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", __func__)); ret = NULL; goto ON_EXIT; } else if (Status == EFI_UNSUPPORTED) { DEBUG ((DEBUG_MANAGEABILITY, "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 ((ResponseMsg.Headers != NULL) && (Headers != NULL) && (HeaderCount != NULL)) { *Headers = cloneHttpHeaders (&ResponseMsg, HeaderCount); } 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 * postUriFromServiceEx ( redfishService *service, const char *uri, const char *content, size_t contentLength, const char *contentType, EFI_HTTP_HEADER **Headers OPTIONAL, UINTN *HeaderCount OPTIONAL, 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; if (HeaderCount != NULL) { *HeaderCount = 0; } if (Headers != NULL) { *Headers = NULL; } url = makeUrlForService (service, uri); if (!url) { return NULL; } DEBUG ((DEBUG_MANAGEABILITY, "%a: %a\n", __func__, 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)) { // // If there is no response to handle, go to error exit. // if (ResponseMsg.Data.Response == 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) { goto ON_EXIT; } // // The caller shall take the responsibility to free the buffer. // **StatusCode = ResponseMsg.Data.Response->StatusCode; } if ((ResponseMsg.Headers != NULL) && (Headers != NULL) && (HeaderCount != NULL)) { *Headers = cloneHttpHeaders (&ResponseMsg, HeaderCount); } if ((ResponseMsg.BodyLength != 0) && (ResponseMsg.Body != NULL)) { ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, NULL); } // // Step 6: Parsing the HttpHeader to retrieve the X-Auth-Token if the HTTP StatusCode is correct. // if ((ResponseMsg.Data.Response != NULL) && ((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); } } 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 * getUriFromService ( redfishService *service, const char *uri, EFI_HTTP_STATUS_CODE **StatusCode ) { return getUriFromServiceEx (service, uri, NULL, NULL, StatusCode); } json_t * patchUriFromService ( redfishService *service, const char *uri, const char *content, EFI_HTTP_STATUS_CODE **StatusCode ) { return patchUriFromServiceEx (service, uri, content, NULL, NULL, StatusCode); } json_t * postUriFromService ( redfishService *service, const char *uri, const char *content, size_t contentLength, const char *contentType, EFI_HTTP_STATUS_CODE **StatusCode ) { return postUriFromServiceEx (service, uri, content, contentLength, contentType, NULL, NULL, StatusCode); } json_t * deleteUriFromServiceEx ( 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]; size_t contentLength; ret = NULL; if ((service == NULL) || (uri == NULL) || (StatusCode == NULL)) { return NULL; } *StatusCode = NULL; url = makeUrlForService (service, uri); if (!url) { return NULL; } DEBUG ((DEBUG_MANAGEABILITY, "libredfish: deleteUriFromService(): %a\n", url)); // // Step 1: Create HTTP request message with 4 headers: // HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service->basicAuthStr) ? 8 : 7); 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); Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", "application/json"); ASSERT_EFI_ERROR (Status); if (content != NULL) { contentLength = strlen (content); 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) { 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; if (content != NULL) { 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)) { 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; } json_t * deleteUriFromService ( redfishService *service, const char *uri, EFI_HTTP_STATUS_CODE **StatusCode ) { return deleteUriFromServiceEx (service, uri, NULL, StatusCode); } 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); if (url == NULL) { return NULL; } 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); }