/** @file
This file is cloned from DMTF libredfish library tag v1.0.0 and maintained
by EDKII.
//----------------------------------------------------------------------------
// Copyright Notice:
// Copyright 2017 Distributed Management Task Force, Inc. All rights reserved.
// License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libredfish/LICENSE.md
//----------------------------------------------------------------------------
Copyright (c) 2019, Intel Corporation. All rights reserved.
(C) Copyright 2021 Hewlett Packard Enterprise Development LP
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#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;
}
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);
}