--- /dev/null
+/** @file\r
+ The implementation for the 'http' Shell command.\r
+\r
+ Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>\r
+ Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved. <BR>\r
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
+ Copyright (c) 2020, Broadcom. All rights reserved. <BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "Http.h"\r
+\r
+#define IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH 32\r
+\r
+//\r
+// Constant strings and definitions related to the message\r
+// indicating the amount of progress in the dowloading of a HTTP file.\r
+//\r
+\r
+//\r
+// Number of steps in the progression slider.\r
+//\r
+#define HTTP_PROGRESS_SLIDER_STEPS \\r
+ ((sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) - 3)\r
+\r
+//\r
+// Size in number of characters plus one (final zero) of the message to\r
+// indicate the progress of an HTTP download. The format is "[(progress slider:\r
+// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There\r
+// are thus the number of characters in HTTP_PROGR_FRAME[] plus 11 characters\r
+// (2 // spaces, "Kb" and seven characters for the number of KBytes).\r
+//\r
+#define HTTP_PROGRESS_MESSAGE_SIZE \\r
+ ((sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) + 12)\r
+\r
+//\r
+// Buffer size. Note that larger buffer does not mean better speed.\r
+//\r
+#define DEFAULT_BUF_SIZE SIZE_32KB\r
+#define MAX_BUF_SIZE SIZE_4MB\r
+\r
+#define MIN_PARAM_COUNT 2\r
+#define MAX_PARAM_COUNT 4\r
+#define NEED_REDIRECTION(Code) \\r
+ (((Code >= HTTP_STATUS_300_MULTIPLE_CHOICES) \\r
+ && (Code <= HTTP_STATUS_307_TEMPORARY_REDIRECT)) \\r
+ || (Code == HTTP_STATUS_308_PERMANENT_REDIRECT))\r
+\r
+#define CLOSE_HTTP_HANDLE(ControllerHandle,HttpChildHandle) \\r
+ do { \\r
+ if (HttpChildHandle) { \\r
+ CloseProtocolAndDestroyServiceChild ( \\r
+ ControllerHandle, \\r
+ &gEfiHttpServiceBindingProtocolGuid, \\r
+ &gEfiHttpProtocolGuid, \\r
+ HttpChildHandle \\r
+ ); \\r
+ HttpChildHandle = NULL; \\r
+ } \\r
+ } while (0)\r
+\r
+typedef enum {\r
+ HdrHost,\r
+ HdrConn,\r
+ HdrAgent,\r
+ HdrMax\r
+} HDR_TYPE;\r
+\r
+#define USER_AGENT_HDR "Mozilla/5.0 (EDK2; Linux) Gecko/20100101 Firefox/79.0"\r
+\r
+#define TIMER_MAX_TIMEOUT_S 10\r
+\r
+//\r
+// File name to use when Uri ends with "/".\r
+//\r
+#define DEFAULT_HTML_FILE L"index.html"\r
+#define DEFAULT_HTTP_PROTO L"http"\r
+\r
+//\r
+// String to delete the HTTP progress message to be able to update it :\r
+// (HTTP_PROGRESS_MESSAGE_SIZE-1) '\b'.\r
+//\r
+#define HTTP_PROGRESS_DEL \\r
+ L"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\\r
+\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"\r
+\r
+#define HTTP_KB L"\b\b\b\b\b\b\b\b\b\b"\r
+//\r
+// Frame for the progression slider.\r
+//\r
+#define HTTP_PROGR_FRAME L"[ ]"\r
+\r
+//\r
+// Improve readability by using these macros.\r
+//\r
+#define PRINT_HII(token,...) \\r
+ ShellPrintHiiEx (\\r
+ -1, -1, NULL, token, mHttpHiiHandle, __VA_ARGS__)\r
+\r
+#define PRINT_HII_APP(token,value) \\r
+ PRINT_HII (token, HTTP_APP_NAME, value)\r
+\r
+//\r
+// TimeBaseLib.h constants.\r
+// These will be removed once the library gets fixed.\r
+//\r
+\r
+//\r
+// Define EPOCH (1970-JANUARY-01) in the Julian Date representation.\r
+//\r
+#define EPOCH_JULIAN_DATE 2440588\r
+\r
+//\r
+// Seconds per unit.\r
+//\r
+#define SEC_PER_MIN ((UINTN) 60)\r
+#define SEC_PER_HOUR ((UINTN) 3600)\r
+#define SEC_PER_DAY ((UINTN) 86400)\r
+\r
+//\r
+// String descriptions for server errors.\r
+//\r
+STATIC CONST CHAR16 *ErrStatusDesc[] =\r
+{\r
+ L"400 Bad Request",\r
+ L"401 Unauthorized",\r
+ L"402 Payment required",\r
+ L"403 Forbidden",\r
+ L"404 Not Found",\r
+ L"405 Method not allowed",\r
+ L"406 Not acceptable",\r
+ L"407 Proxy authentication required",\r
+ L"408 Request time out",\r
+ L"409 Conflict",\r
+ L"410 Gone",\r
+ L"411 Length required",\r
+ L"412 Precondition failed",\r
+ L"413 Request entity too large",\r
+ L"414 Request URI to large",\r
+ L"415 Unsupported media type",\r
+ L"416 Requested range not satisfied",\r
+ L"417 Expectation failed",\r
+ L"500 Internal server error",\r
+ L"501 Not implemented",\r
+ L"502 Bad gateway",\r
+ L"503 Service unavailable",\r
+ L"504 Gateway timeout",\r
+ L"505 HTTP version not supported"\r
+};\r
+\r
+STATIC CONST SHELL_PARAM_ITEM ParamList[] = {\r
+ {L"-i", TypeValue},\r
+ {L"-k", TypeFlag},\r
+ {L"-l", TypeValue},\r
+ {L"-m", TypeFlag},\r
+ {L"-s", TypeValue},\r
+ {L"-t", TypeValue},\r
+ {NULL , TypeMax}\r
+};\r
+\r
+//\r
+// Local File Handle.\r
+//\r
+STATIC SHELL_FILE_HANDLE mFileHandle = NULL;\r
+\r
+//\r
+// Path of the local file, Unicode encoded.\r
+//\r
+STATIC CONST CHAR16 *mLocalFilePath;\r
+\r
+STATIC BOOLEAN gRequestCallbackComplete = FALSE;\r
+STATIC BOOLEAN gResponseCallbackComplete = FALSE;\r
+\r
+STATIC BOOLEAN gHttpError;\r
+\r
+EFI_HII_HANDLE mHttpHiiHandle;\r
+\r
+//\r
+// Functions declarations.\r
+//\r
+\r
+/**\r
+ Check and convert the UINT16 option values of the 'http' command.\r
+\r
+ @param[in] ValueStr Value as an Unicode encoded string.\r
+ @param[out] Value UINT16 value.\r
+\r
+ @retval TRUE The value was returned.\r
+ @retval FALSE A parsing error occured.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+StringToUint16 (\r
+ IN CONST CHAR16 *ValueStr,\r
+ OUT UINT16 *Value\r
+ );\r
+\r
+/**\r
+ Get the name of the NIC.\r
+\r
+ @param[in] ControllerHandle The network physical device handle.\r
+ @param[in] NicNumber The network physical device number.\r
+ @param[out] NicName Address where to store the NIC name.\r
+ The memory area has to be at least\r
+ IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH\r
+ double byte wide.\r
+\r
+ @retval EFI_SUCCESS The name of the NIC was returned.\r
+ @retval Others The creation of the child for the Managed\r
+ Network Service failed or the opening of\r
+ the Managed Network Protocol failed or\r
+ the operational parameters for the\r
+ Managed Network Protocol could not be\r
+ read.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetNicName (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NicNumber,\r
+ OUT CHAR16 *NicName\r
+ );\r
+\r
+/**\r
+ Create a child for the service identified by its service binding protocol GUID\r
+ and get from the child the interface of the protocol identified by its GUID.\r
+\r
+ @param[in] ControllerHandle Controller handle.\r
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
+ service to be created.\r
+ @param[in] ProtocolGuid GUID of the protocol to be open.\r
+ @param[out] ChildHandle Address where the handler of the\r
+ created child is returned. NULL is\r
+ returned in case of error.\r
+ @param[out] Interface Address where a pointer to the\r
+ protocol interface is returned in\r
+ case of success.\r
+\r
+ @retval EFI_SUCCESS The child was created and the protocol opened.\r
+ @retval Others Either the creation of the child or the opening\r
+ of the protocol failed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+CreateServiceChildAndOpenProtocol (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ OUT EFI_HANDLE *ChildHandle,\r
+ OUT VOID **Interface\r
+ );\r
+\r
+/**\r
+ Close the protocol identified by its GUID on the child handle of the service\r
+ identified by its service binding protocol GUID, then destroy the child\r
+ handle.\r
+\r
+ @param[in] ControllerHandle Controller handle.\r
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
+ service to be destroyed.\r
+ @param[in] ProtocolGuid GUID of the protocol to be closed.\r
+ @param[in] ChildHandle Handle of the child to be destroyed.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+CloseProtocolAndDestroyServiceChild (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ IN EFI_HANDLE ChildHandle\r
+ );\r
+\r
+/**\r
+ Worker function that download the data of a file from an HTTP server given\r
+ the path of the file and its size.\r
+\r
+ @param[in] Context A pointer to the download context.\r
+\r
+ @retval EFI_SUCCESS The file was downloaded.\r
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
+ @retval Others The downloading of the file\r
+ from the server failed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DownloadFile (\r
+ IN HTTP_DOWNLOAD_CONTEXT *Context,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN CHAR16 *NicName\r
+ );\r
+\r
+/**\r
+ Cleans off leading and trailing spaces and tabs.\r
+\r
+ @param[in] String pointer to the string to trim them off.\r
+\r
+ @retval EFI_SUCCESS No errors.\r
+ @retval EFI_INVALID_PARAMETER String pointer is NULL.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+TrimSpaces (\r
+ IN CHAR16 *String\r
+ )\r
+{\r
+ CHAR16 *Str;\r
+ UINTN Len;\r
+\r
+ ASSERT (String != NULL);\r
+\r
+ if (String == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Str = String;\r
+\r
+ //\r
+ // Remove any whitespace at the beginning of the Str.\r
+ //\r
+ while (*Str == L' ' || *Str == L'\t') {\r
+ Str++;\r
+ }\r
+\r
+ //\r
+ // Remove any whitespace at the end of the Str.\r
+ //\r
+ do {\r
+ Len = StrLen (Str);\r
+ if (!Len || (Str[Len - 1] != L' ' && Str[Len - 1] != '\t')) {\r
+ break;\r
+ }\r
+\r
+ Str[Len - 1] = CHAR_NULL;\r
+ } while (Len);\r
+\r
+ CopyMem (String, Str, StrSize (Str));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+//\r
+// Callbacks for request and response.\r
+// We just acknowledge that operation has completed here.\r
+//\r
+\r
+/**\r
+ Callback to set the request completion flag.\r
+\r
+ @param[in] Event: The event.\r
+ @param[in] Context: pointer to Notification Context.\r
+ **/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+RequestCallback (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ gRequestCallbackComplete = TRUE;\r
+}\r
+\r
+/**\r
+ Callback to set the response completion flag.\r
+ @param[in] Event: The event.\r
+ @param[in] Context: pointer to Notification Context.\r
+ **/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+ResponseCallback (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ gResponseCallbackComplete = TRUE;\r
+}\r
+\r
+//\r
+// Set of functions from TimeBaseLib.\r
+// This will be removed once TimeBaseLib is enabled for ShellPkg.\r
+//\r
+\r
+/**\r
+ Calculate Epoch days.\r
+\r
+ @param[in] Time - a pointer to the EFI_TIME abstraction.\r
+\r
+ @retval Number of days elapsed since EPOCH_JULIAN_DAY.\r
+ **/\r
+STATIC\r
+UINTN\r
+EfiGetEpochDays (\r
+ IN EFI_TIME *Time\r
+ )\r
+{\r
+ UINTN a;\r
+ UINTN y;\r
+ UINTN m;\r
+ //\r
+ // Absolute Julian Date representation of the supplied Time.\r
+ //\r
+ UINTN JulianDate;\r
+ //\r
+ // Number of days elapsed since EPOCH_JULIAN_DAY.\r
+ //\r
+ UINTN EpochDays;\r
+\r
+ a = (14 - Time->Month) / 12 ;\r
+ y = Time->Year + 4800 - a;\r
+ m = Time->Month + (12 * a) - 3;\r
+\r
+ JulianDate = Time->Day + ((153 * m + 2) / 5) + (365 * y) + (y / 4) -\r
+ (y / 100) + (y / 400) - 32045;\r
+\r
+ ASSERT (JulianDate >= EPOCH_JULIAN_DATE);\r
+ EpochDays = JulianDate - EPOCH_JULIAN_DATE;\r
+\r
+ return EpochDays;\r
+}\r
+\r
+/**\r
+ Converts EFI_TIME to Epoch seconds\r
+ (elapsed since 1970 JANUARY 01, 00:00:00 UTC).\r
+\r
+ @param[in] Time: a pointer to EFI_TIME abstraction.\r
+ **/\r
+STATIC\r
+UINTN\r
+EFIAPI\r
+EfiTimeToEpoch (\r
+ IN EFI_TIME *Time\r
+ )\r
+{\r
+ //\r
+ // Number of days elapsed since EPOCH_JULIAN_DAY.\r
+ //\r
+ UINTN EpochDays;\r
+ UINTN EpochSeconds;\r
+\r
+ EpochDays = EfiGetEpochDays (Time);\r
+\r
+ EpochSeconds = (EpochDays * SEC_PER_DAY) +\r
+ ((UINTN)Time->Hour * SEC_PER_HOUR) +\r
+ (Time->Minute * SEC_PER_MIN) + Time->Second;\r
+\r
+ return EpochSeconds;\r
+}\r
+\r
+/**\r
+ Function for 'http' command.\r
+\r
+ @param[in] ImageHandle Handle to the Image (NULL if Internal).\r
+ @param[in] SystemTable Pointer to the System Table (NULL if Internal).\r
+\r
+ @retval SHELL_SUCCESS The 'http' command completed successfully.\r
+ @retval SHELL_ABORTED The Shell Library initialization failed.\r
+ @retval SHELL_INVALID_PARAMETER At least one of the command's arguments is\r
+ not valid.\r
+ @retval SHELL_OUT_OF_RESOURCES A memory allocation failed.\r
+ @retval SHELL_NOT_FOUND Network Interface Card not found.\r
+ @retval SHELL_UNSUPPORTED Command was valid, but the server returned\r
+ a status code indicating some error.\r
+ Examine the file requested for error body.\r
+**/\r
+SHELL_STATUS\r
+RunHttp (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *CheckPackage;\r
+ UINTN ParamCount;\r
+ UINTN HandleCount;\r
+ UINTN NicNumber;\r
+ UINTN InitialSize;\r
+ UINTN ParamOffset;\r
+ UINTN StartSize;\r
+ CHAR16 *ProblemParam;\r
+ CHAR16 NicName[IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH];\r
+ CHAR16 *Walker1;\r
+ CHAR16 *VStr;\r
+ CONST CHAR16 *UserNicName;\r
+ CONST CHAR16 *ValueStr;\r
+ CONST CHAR16 *RemoteFilePath;\r
+ CONST CHAR16 *Walker;\r
+ EFI_HTTPv4_ACCESS_POINT IPv4Node;\r
+ EFI_HANDLE *Handles;\r
+ EFI_HANDLE ControllerHandle;\r
+ HTTP_DOWNLOAD_CONTEXT Context;\r
+ BOOLEAN NicFound;\r
+\r
+ ProblemParam = NULL;\r
+ RemoteFilePath = NULL;\r
+ NicFound = FALSE;\r
+ Handles = NULL;\r
+\r
+ //\r
+ // Initialize the Shell library (we must be in non-auto-init...).\r
+ //\r
+ ParamOffset = 0;\r
+ gHttpError = FALSE;\r
+\r
+ Status = ShellInitialize ();\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return SHELL_ABORTED;\r
+ }\r
+\r
+ ZeroMem (&Context, sizeof (Context));\r
+\r
+ //\r
+ // Parse the command line.\r
+ //\r
+ Status = ShellCommandLineParse (\r
+ ParamList,\r
+ &CheckPackage,\r
+ &ProblemParam,\r
+ TRUE\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ if ((Status == EFI_VOLUME_CORRUPTED)\r
+ && (ProblemParam != NULL))\r
+ {\r
+ PRINT_HII_APP (STRING_TOKEN (STR_GEN_PROBLEM), ProblemParam);\r
+ SHELL_FREE_NON_NULL (ProblemParam);\r
+ } else {\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Check the number of parameters.\r
+ //\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ ParamCount = ShellCommandLineGetCount (CheckPackage);\r
+ if (ParamCount > MAX_PARAM_COUNT) {\r
+ PRINT_HII_APP (STRING_TOKEN (STR_GEN_TOO_MANY), NULL);\r
+ goto Error;\r
+ }\r
+\r
+ if (ParamCount < MIN_PARAM_COUNT) {\r
+ PRINT_HII_APP (STRING_TOKEN (STR_GEN_TOO_FEW), NULL);\r
+ goto Error;\r
+ }\r
+\r
+ ZeroMem (&Context.HttpConfigData, sizeof (Context.HttpConfigData));\r
+ ZeroMem (&IPv4Node, sizeof (IPv4Node));\r
+ IPv4Node.UseDefaultAddress = TRUE;\r
+\r
+ Context.HttpConfigData.HttpVersion = HttpVersion11;\r
+ Context.HttpConfigData.AccessPoint.IPv4Node = &IPv4Node;\r
+\r
+ //\r
+ // Get the host address (not necessarily IPv4 format).\r
+ //\r
+ ValueStr = ShellCommandLineGetRawValue (CheckPackage, 1);\r
+ if (!ValueStr) {\r
+ PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
+ goto Error;\r
+ } else {\r
+ StartSize = 0;\r
+ TrimSpaces ((CHAR16 *)ValueStr);\r
+ if (!StrStr (ValueStr, L"://")) {\r
+ Context.ServerAddrAndProto = StrnCatGrow (\r
+ &Context.ServerAddrAndProto,\r
+ &StartSize,\r
+ DEFAULT_HTTP_PROTO,\r
+ StrLen (DEFAULT_HTTP_PROTO)\r
+ );\r
+ Context.ServerAddrAndProto = StrnCatGrow (\r
+ &Context.ServerAddrAndProto,\r
+ &StartSize,\r
+ L"://",\r
+ StrLen (L"://")\r
+ );\r
+ VStr = (CHAR16 *)ValueStr;\r
+ } else {\r
+ VStr = StrStr (ValueStr, L"://") + StrLen (L"://");\r
+ }\r
+\r
+ for (Walker1 = VStr; *Walker1; Walker1++) {\r
+ if (*Walker1 == L'/') {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (*Walker1 == L'/') {\r
+ ParamOffset = 1;\r
+ RemoteFilePath = Walker1;\r
+ }\r
+\r
+ Context.ServerAddrAndProto = StrnCatGrow (\r
+ &Context.ServerAddrAndProto,\r
+ &StartSize,\r
+ ValueStr,\r
+ StrLen (ValueStr) - StrLen (Walker1)\r
+ );\r
+ if (!Context.ServerAddrAndProto) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ if (!RemoteFilePath) {\r
+ RemoteFilePath = ShellCommandLineGetRawValue (CheckPackage, 2);\r
+ if (!RemoteFilePath) {\r
+ //\r
+ // If no path given, assume just "/".\r
+ //\r
+ RemoteFilePath = L"/";\r
+ }\r
+ }\r
+\r
+ TrimSpaces ((CHAR16 *)RemoteFilePath);\r
+\r
+ if (ParamCount == MAX_PARAM_COUNT - ParamOffset) {\r
+ mLocalFilePath = ShellCommandLineGetRawValue (\r
+ CheckPackage,\r
+ MAX_PARAM_COUNT - 1 - ParamOffset\r
+ );\r
+ } else {\r
+ Walker = RemoteFilePath + StrLen (RemoteFilePath);\r
+ while ((--Walker) >= RemoteFilePath) {\r
+ if ((*Walker == L'\\') ||\r
+ (*Walker == L'/' ) ) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ mLocalFilePath = Walker + 1;\r
+ }\r
+\r
+ if (!StrLen (mLocalFilePath)) {\r
+ mLocalFilePath = DEFAULT_HTML_FILE;\r
+ }\r
+\r
+ InitialSize = 0;\r
+ Context.Uri = StrnCatGrow (\r
+ &Context.Uri,\r
+ &InitialSize,\r
+ RemoteFilePath,\r
+ StrLen (RemoteFilePath)\r
+ );\r
+ if (!Context.Uri) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Get the name of the Network Interface Card to be used if any.\r
+ //\r
+ UserNicName = ShellCommandLineGetValue (CheckPackage, L"-i");\r
+\r
+ ValueStr = ShellCommandLineGetValue (CheckPackage, L"-l");\r
+ if ((ValueStr != NULL)\r
+ && (!StringToUint16 (\r
+ ValueStr,\r
+ &Context.HttpConfigData.AccessPoint.IPv4Node->LocalPort\r
+ )\r
+ ))\r
+ {\r
+ goto Error;\r
+ }\r
+\r
+ Context.BufferSize = DEFAULT_BUF_SIZE;\r
+\r
+ ValueStr = ShellCommandLineGetValue (CheckPackage, L"-s");\r
+ if (ValueStr != NULL) {\r
+ Context.BufferSize = ShellStrToUintn (ValueStr);\r
+ if (!Context.BufferSize || Context.BufferSize > MAX_BUF_SIZE) {\r
+ PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (CheckPackage, L"-t");\r
+ if (ValueStr != NULL) {\r
+ Context.HttpConfigData.TimeOutMillisec = (UINT32)ShellStrToUintn (ValueStr);\r
+ }\r
+\r
+ //\r
+ // Locate all HTTP Service Binding protocols.\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ if (EFI_ERROR (Status) || (HandleCount == 0)) {\r
+ PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NO_NIC), NULL);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+\r
+ goto Error;\r
+ }\r
+\r
+ Status = EFI_NOT_FOUND;\r
+\r
+ Context.Flags = 0;\r
+ if (ShellCommandLineGetFlag (CheckPackage, L"-m")) {\r
+ Context.Flags |= DL_FLAG_TIME;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (CheckPackage, L"-k")) {\r
+ Context.Flags |= DL_FLAG_KEEP_BAD;\r
+ }\r
+\r
+ for (NicNumber = 0;\r
+ (NicNumber < HandleCount) && (Status != EFI_SUCCESS);\r
+ NicNumber++)\r
+ {\r
+ ControllerHandle = Handles[NicNumber];\r
+\r
+ Status = GetNicName (ControllerHandle, NicNumber, NicName);\r
+ if (EFI_ERROR (Status)) {\r
+ PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NIC_NAME), NicNumber, Status);\r
+ continue;\r
+ }\r
+\r
+ if (UserNicName != NULL) {\r
+ if (StrCmp (NicName, UserNicName) != 0) {\r
+ Status = EFI_NOT_FOUND;\r
+ continue;\r
+ }\r
+\r
+ NicFound = TRUE;\r
+ }\r
+\r
+ Status = DownloadFile (&Context, ControllerHandle, NicName);\r
+ PRINT_HII (STRING_TOKEN (STR_GEN_CRLF), NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ PRINT_HII (\r
+ STRING_TOKEN (STR_HTTP_ERR_DOWNLOAD),\r
+ RemoteFilePath,\r
+ NicName,\r
+ Status\r
+ );\r
+ //\r
+ // If a user aborted the operation,\r
+ // do not try another controller.\r
+ //\r
+ if (Status == EFI_ABORTED) {\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ if (gHttpError) {\r
+ //\r
+ // This is not related to connection, so no need to repeat with\r
+ // another interface.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ if ((UserNicName != NULL) && (!NicFound)) {\r
+ PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NIC_NOT_FOUND), UserNicName);\r
+ }\r
+\r
+Error:\r
+ ShellCommandLineFreeVarList (CheckPackage);\r
+ SHELL_FREE_NON_NULL (Handles);\r
+ SHELL_FREE_NON_NULL (Context.ServerAddrAndProto);\r
+ SHELL_FREE_NON_NULL (Context.Uri);\r
+\r
+ return Status & ~MAX_BIT;\r
+}\r
+\r
+/**\r
+ Check and convert the UINT16 option values of the 'http' command\r
+\r
+ @param[in] ValueStr Value as an Unicode encoded string\r
+ @param[out] Value UINT16 value\r
+\r
+ @retval TRUE The value was returned.\r
+ @retval FALSE A parsing error occured.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+StringToUint16 (\r
+ IN CONST CHAR16 *ValueStr,\r
+ OUT UINT16 *Value\r
+ )\r
+{\r
+ UINTN Val;\r
+\r
+ Val = ShellStrToUintn (ValueStr);\r
+ if (Val > MAX_UINT16) {\r
+ PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
+ return FALSE;\r
+ }\r
+\r
+ *Value = (UINT16)Val;\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Get the name of the NIC.\r
+\r
+ @param[in] ControllerHandle The network physical device handle.\r
+ @param[in] NicNumber The network physical device number.\r
+ @param[out] NicName Address where to store the NIC name.\r
+ The memory area has to be at least\r
+ IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH\r
+ double byte wide.\r
+\r
+ @retval EFI_SUCCESS The name of the NIC was returned.\r
+ @retval Others The creation of the child for the Managed\r
+ Network Service failed or the opening of\r
+ the Managed Network Protocol failed or\r
+ the operational parameters for the\r
+ Managed Network Protocol could not be\r
+ read.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetNicName (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NicNumber,\r
+ OUT CHAR16 *NicName\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE MnpHandle;\r
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
+ EFI_SIMPLE_NETWORK_MODE SnpMode;\r
+\r
+ Status = CreateServiceChildAndOpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ &MnpHandle,\r
+ (VOID**)&Mnp\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Status = Mnp->GetModeData (Mnp, NULL, &SnpMode);\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {\r
+ goto Error;\r
+ }\r
+\r
+ UnicodeSPrint (\r
+ NicName,\r
+ IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH,\r
+ SnpMode.IfType == NET_IFTYPE_ETHERNET ? L"eth%d" : L"unk%d",\r
+ NicNumber\r
+ );\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Error:\r
+\r
+ if (MnpHandle != NULL) {\r
+ CloseProtocolAndDestroyServiceChild (\r
+ ControllerHandle,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ MnpHandle\r
+ );\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Create a child for the service identified by its service binding protocol GUID\r
+ and get from the child the interface of the protocol identified by its GUID.\r
+\r
+ @param[in] ControllerHandle Controller handle.\r
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
+ service to be created.\r
+ @param[in] ProtocolGuid GUID of the protocol to be open.\r
+ @param[out] ChildHandle Address where the handler of the\r
+ created child is returned. NULL is\r
+ returned in case of error.\r
+ @param[out] Interface Address where a pointer to the\r
+ protocol interface is returned in\r
+ case of success.\r
+\r
+ @retval EFI_SUCCESS The child was created and the protocol opened.\r
+ @retval Others Either the creation of the child or the opening\r
+ of the protocol failed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+CreateServiceChildAndOpenProtocol (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ OUT EFI_HANDLE *ChildHandle,\r
+ OUT VOID **Interface\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ *ChildHandle = NULL;\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ ServiceBindingProtocolGuid,\r
+ ChildHandle\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->OpenProtocol (\r
+ *ChildHandle,\r
+ ProtocolGuid,\r
+ Interface,\r
+ gImageHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ NetLibDestroyServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ ServiceBindingProtocolGuid,\r
+ *ChildHandle\r
+ );\r
+ *ChildHandle = NULL;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Close the protocol identified by its GUID on the child handle of the service\r
+ identified by its service binding protocol GUID, then destroy the child\r
+ handle.\r
+\r
+ @param[in] ControllerHandle Controller handle.\r
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
+ service to be destroyed.\r
+ @param[in] ProtocolGuid GUID of the protocol to be closed.\r
+ @param[in] ChildHandle Handle of the child to be destroyed.\r
+**/\r
+STATIC\r
+VOID\r
+CloseProtocolAndDestroyServiceChild (\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_GUID *ServiceBindingProtocolGuid,\r
+ IN EFI_GUID *ProtocolGuid,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ gBS->CloseProtocol (\r
+ ChildHandle,\r
+ ProtocolGuid,\r
+ gImageHandle,\r
+ ControllerHandle\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ ServiceBindingProtocolGuid,\r
+ ChildHandle\r
+ );\r
+}\r
+\r
+/**\r
+ Wait until operation completes. Completion is indicated by\r
+ setting of an appropriate variable.\r
+\r
+ @param[in] Context A pointer to the HTTP download context.\r
+ @param[in, out] CallBackComplete A pointer to the callback completion\r
+ variable set by the callback.\r
+\r
+ @retval EFI_SUCCESS Callback signalled completion.\r
+ @retval EFI_TIMEOUT Timed out waiting for completion.\r
+ @retval Others Error waiting for completion.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+WaitForCompletion (\r
+ IN HTTP_DOWNLOAD_CONTEXT *Context,\r
+ IN OUT BOOLEAN *CallBackComplete\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT WaitEvt;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Use a timer to measure timeout. Cannot use Stall here!\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &WaitEvt\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->SetTimer (\r
+ WaitEvt,\r
+ TimerRelative,\r
+ EFI_TIMER_PERIOD_SECONDS (TIMER_MAX_TIMEOUT_S)\r
+ );\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ while (! *CallBackComplete\r
+ && (!EFI_ERROR (Status))\r
+ && EFI_ERROR (gBS->CheckEvent (WaitEvt)))\r
+ {\r
+ Status = Context->Http->Poll (Context->Http);\r
+ if (!Context->ContentDownloaded\r
+ && CallBackComplete == &gResponseCallbackComplete)\r
+ {\r
+ //\r
+ // An HTTP server may just send a response redirection header.\r
+ // In this case, don't wait for the event as\r
+ // it might never happen and we waste 10s waiting.\r
+ // Note that at this point Response may not has been populated,\r
+ // so it needs to be checked first.\r
+ //\r
+ if (Context->ResponseToken.Message\r
+ && Context->ResponseToken.Message->Data.Response\r
+ && (NEED_REDIRECTION (\r
+ Context->ResponseToken.Message->Data.Response->StatusCode\r
+ )\r
+ ))\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ gBS->SetTimer (WaitEvt, TimerCancel, 0);\r
+ gBS->CloseEvent (WaitEvt);\r
+\r
+ if (*CallBackComplete) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = EFI_TIMEOUT;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Generate and send a request to the http server.\r
+\r
+ @param[in] Context HTTP download context.\r
+ @param[in] DownloadUrl Fully qualified URL to be downloaded.\r
+\r
+ @retval EFI_SUCCESS Request has been sent successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid URL.\r
+ @retval EFI_OUT_OF_RESOURCES Out of memory.\r
+ @retval EFI_DEVICE_ERROR If HTTPS is used, this probably\r
+ means that TLS support either was not\r
+ installed or not configured.\r
+ @retval Others Error sending the request.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SendRequest (\r
+ IN HTTP_DOWNLOAD_CONTEXT *Context,\r
+ IN CHAR16 *DownloadUrl\r
+ )\r
+{\r
+ EFI_HTTP_REQUEST_DATA RequestData;\r
+ EFI_HTTP_HEADER RequestHeader[HdrMax];\r
+ EFI_HTTP_MESSAGE RequestMessage;\r
+ EFI_STATUS Status;\r
+ CHAR16 *Host;\r
+ UINTN StringSize;\r
+\r
+ ZeroMem (&RequestData, sizeof (RequestData));\r
+ ZeroMem (&RequestHeader, sizeof (RequestHeader));\r
+ ZeroMem (&RequestMessage, sizeof (RequestMessage));\r
+ ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));\r
+\r
+ RequestHeader[HdrHost].FieldName = "Host";\r
+ RequestHeader[HdrConn].FieldName = "Connection";\r
+ RequestHeader[HdrAgent].FieldName = "User-Agent";\r
+\r
+ Host = (CHAR16 *)Context->ServerAddrAndProto;\r
+ while (*Host != CHAR_NULL && *Host != L'/') {\r
+ Host++;\r
+ }\r
+\r
+ if (*Host == CHAR_NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Get the next slash.\r
+ //\r
+ Host++;\r
+ //\r
+ // And now the host name.\r
+ //\r
+ Host++;\r
+\r
+ StringSize = StrLen (Host) + 1;\r
+ RequestHeader[HdrHost].FieldValue = AllocatePool (StringSize);\r
+ if (!RequestHeader[HdrHost].FieldValue) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ UnicodeStrToAsciiStrS (\r
+ Host,\r
+ RequestHeader[HdrHost].FieldValue,\r
+ StringSize\r
+ );\r
+\r
+ RequestHeader[HdrConn].FieldValue = "close";\r
+ RequestHeader[HdrAgent].FieldValue = USER_AGENT_HDR;\r
+ RequestMessage.HeaderCount = HdrMax;\r
+\r
+ RequestData.Method = HttpMethodGet;\r
+ RequestData.Url = DownloadUrl;\r
+\r
+ RequestMessage.Data.Request = &RequestData;\r
+ RequestMessage.Headers = RequestHeader;\r
+ RequestMessage.BodyLength = 0;\r
+ RequestMessage.Body = NULL;\r
+ Context->RequestToken.Event = NULL;\r
+\r
+ //\r
+ // Completion callback event to be set when Request completes.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ RequestCallback,\r
+ Context,\r
+ &Context->RequestToken.Event\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Context->RequestToken.Status = EFI_SUCCESS;\r
+ Context->RequestToken.Message = &RequestMessage;\r
+ gRequestCallbackComplete = FALSE;\r
+ Status = Context->Http->Request (Context->Http, &Context->RequestToken);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Status = WaitForCompletion (Context, &gRequestCallbackComplete);\r
+ if (EFI_ERROR (Status)) {\r
+ Context->Http->Cancel (Context->Http, &Context->RequestToken);\r
+ }\r
+\r
+Error:\r
+ SHELL_FREE_NON_NULL (RequestHeader[HdrHost].FieldValue);\r
+ if (Context->RequestToken.Event) {\r
+ gBS->CloseEvent (Context->RequestToken.Event);\r
+ ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Update the progress of a file download\r
+ This procedure is called each time a new HTTP body portion is received.\r
+\r
+ @param[in] Context HTTP download context.\r
+ @param[in] DownloadLen Portion size, in bytes.\r
+ @param[in] Buffer The pointer to the parsed buffer.\r
+\r
+ @retval EFI_SUCCESS Portion saved.\r
+ @retval Other Error saving the portion.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+SavePortion (\r
+ IN HTTP_DOWNLOAD_CONTEXT *Context,\r
+ IN UINTN DownloadLen,\r
+ IN CHAR8 *Buffer\r
+ )\r
+{\r
+ CHAR16 Progress[HTTP_PROGRESS_MESSAGE_SIZE];\r
+ UINTN NbOfKb;\r
+ UINTN Index;\r
+ UINTN LastStep;\r
+ UINTN Step;\r
+ EFI_STATUS Status;\r
+\r
+ LastStep = 0;\r
+ Step = 0;\r
+\r
+ ShellSetFilePosition (mFileHandle, Context->LastReportedNbOfBytes);\r
+ Status = ShellWriteFile (mFileHandle, &DownloadLen, Buffer);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Context->ContentDownloaded > 0) {\r
+ PRINT_HII (STRING_TOKEN (STR_GEN_CRLF), NULL);\r
+ }\r
+\r
+ PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_WRITE), mLocalFilePath, Status);\r
+ return Status;\r
+ }\r
+\r
+ if (Context->ContentDownloaded == 0) {\r
+ ShellPrintEx (-1, -1, L"%s 0 Kb", HTTP_PROGR_FRAME);\r
+ }\r
+\r
+ Context->ContentDownloaded += DownloadLen;\r
+ NbOfKb = Context->ContentDownloaded >> 10;\r
+\r
+ Progress[0] = L'\0';\r
+ if (Context->ContentLength) {\r
+ LastStep = (Context->LastReportedNbOfBytes * HTTP_PROGRESS_SLIDER_STEPS) /\r
+ Context->ContentLength;\r
+ Step = (Context->ContentDownloaded * HTTP_PROGRESS_SLIDER_STEPS) /\r
+ Context->ContentLength;\r
+ }\r
+\r
+ Context->LastReportedNbOfBytes = Context->ContentDownloaded;\r
+\r
+ if (Step <= LastStep) {\r
+ if (!Context->ContentLength) {\r
+ //\r
+ // Update downloaded size, there is no length info available.\r
+ //\r
+ ShellPrintEx (-1, -1, L"%s", HTTP_KB);\r
+ ShellPrintEx (-1, -1, L"%7d Kb", NbOfKb);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ ShellPrintEx (-1, -1, L"%s", HTTP_PROGRESS_DEL);\r
+\r
+ Status = StrCpyS (Progress, HTTP_PROGRESS_MESSAGE_SIZE, HTTP_PROGR_FRAME);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ for (Index = 1; Index < Step; Index++) {\r
+ Progress[Index] = L'=';\r
+ }\r
+\r
+ if (Step) {\r
+ Progress[Step] = L'>';\r
+ }\r
+\r
+ UnicodeSPrint (\r
+ Progress + (sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) - 1,\r
+ sizeof (Progress) - sizeof (HTTP_PROGR_FRAME),\r
+ L" %7d Kb",\r
+ NbOfKb\r
+ );\r
+\r
+\r
+ ShellPrintEx (-1, -1, L"%s", Progress);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Replace the original Host and Uri with Host and Uri returned by the\r
+ HTTP server in 'Location' header (redirection).\r
+\r
+ @param[in] Location A pointer to the 'Location' string\r
+ provided by HTTP server.\r
+ @param[in] Context A pointer to HTTP download context.\r
+ @param[in] DownloadUrl Fully qualified HTTP URL.\r
+\r
+ @retval EFI_SUCCESS Host and Uri were successfully set.\r
+ @retval EFI_OUT_OF_RESOURCES Error setting Host or Uri.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SetHostURI (\r
+ IN CHAR8 *Location,\r
+ IN HTTP_DOWNLOAD_CONTEXT *Context,\r
+ IN CHAR16 *DownloadUrl\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN StringSize;\r
+ UINTN FirstStep;\r
+ UINTN Idx;\r
+ UINTN Step;\r
+ CHAR8 *Walker;\r
+ CHAR16 *Temp;\r
+ CHAR8 *Tmp;\r
+ CHAR16 *Url;\r
+ BOOLEAN IsAbEmptyUrl;\r
+\r
+ Tmp = NULL;\r
+ Url = NULL;\r
+ IsAbEmptyUrl = FALSE;\r
+ FirstStep = 0;\r
+\r
+ StringSize = (AsciiStrSize (Location) * sizeof (CHAR16));\r
+ Url = AllocateZeroPool (StringSize);\r
+ if (!Url) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = AsciiStrToUnicodeStrS (\r
+ (CONST CHAR8 *)Location,\r
+ Url,\r
+ StringSize\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // If an HTTP server redirects to the same location more than once,\r
+ // then stop attempts and tell it is not reachable.\r
+ //\r
+ if (!StrCmp (Url, DownloadUrl)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Error;\r
+ }\r
+\r
+ if (AsciiStrLen (Location) > 2) {\r
+ //\r
+ // Some servers return 'Location: //server/resource'\r
+ //\r
+ IsAbEmptyUrl = (Location[0] == '/') && (Location[1] == '/');\r
+ if (IsAbEmptyUrl) {\r
+ //\r
+ // Skip first "//"\r
+ //\r
+ Location += 2;\r
+ FirstStep = 1;\r
+ }\r
+ }\r
+\r
+ if (AsciiStrStr (Location, "://") || IsAbEmptyUrl) {\r
+ Idx = 0;\r
+ Walker = Location;\r
+\r
+ for (Step = FirstStep; Step < 2; Step++) {\r
+ for (; *Walker != '/' && *Walker != '\0'; Walker++) {\r
+ Idx++;\r
+ }\r
+\r
+ if (!Step) {\r
+ //\r
+ // Skip "//"\r
+ //\r
+ Idx += 2;\r
+ Walker += 2;\r
+ }\r
+ }\r
+\r
+ Tmp = AllocateZeroPool (Idx + 1);\r
+ if (!Tmp) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ CopyMem (Tmp, Location, Idx);\r
+\r
+ //\r
+ // Location now points to Uri\r
+ //\r
+ Location += Idx;\r
+ StringSize = (Idx + 1) * sizeof (CHAR16);\r
+\r
+ SHELL_FREE_NON_NULL (Context->ServerAddrAndProto);\r
+\r
+ Temp = AllocateZeroPool (StringSize);\r
+ if (!Temp) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ Status = AsciiStrToUnicodeStrS (\r
+ (CONST CHAR8 *)Tmp,\r
+ Temp,\r
+ StringSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ SHELL_FREE_NON_NULL (Temp);\r
+ goto Error;\r
+ }\r
+\r
+ Idx = 0;\r
+ if (IsAbEmptyUrl) {\r
+ Context->ServerAddrAndProto = StrnCatGrow (\r
+ &Context->ServerAddrAndProto,\r
+ &Idx,\r
+ L"http://",\r
+ StrLen (L"http://")\r
+ );\r
+ }\r
+\r
+ Context->ServerAddrAndProto = StrnCatGrow (\r
+ &Context->ServerAddrAndProto,\r
+ &Idx,\r
+ Temp,\r
+ StrLen (Temp)\r
+ );\r
+ SHELL_FREE_NON_NULL (Temp);\r
+ if (!Context->ServerAddrAndProto) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ SHELL_FREE_NON_NULL (Context->Uri);\r
+\r
+ StringSize = AsciiStrSize (Location) * sizeof (CHAR16);\r
+ Context->Uri = AllocateZeroPool (StringSize);\r
+ if (!Context->Uri) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Now make changes to the Uri part.\r
+ //\r
+ Status = AsciiStrToUnicodeStrS (\r
+ (CONST CHAR8 *)Location,\r
+ Context->Uri,\r
+ StringSize\r
+ );\r
+Error:\r
+ SHELL_FREE_NON_NULL (Tmp);\r
+ SHELL_FREE_NON_NULL (Url);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Message parser callback.\r
+ Save a portion of HTTP body.\r
+\r
+ @param[in] EventType Type of event. Can be either\r
+ OnComplete or OnData.\r
+ @param[in] Data A pointer to the buffer with data.\r
+ @param[in] Length Data length of this portion.\r
+ @param[in] Context A pointer to the HTTP download context.\r
+\r
+ @retval EFI_SUCCESS The portion was processed successfully.\r
+ @retval Other Error returned by SavePortion.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ParseMsg (\r
+ IN HTTP_BODY_PARSE_EVENT EventType,\r
+ IN CHAR8 *Data,\r
+ IN UINTN Length,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ if ((Data == NULL)\r
+ || (EventType == BodyParseEventOnComplete)\r
+ || (Context == NULL))\r
+ {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return SavePortion (Context, Length, Data);\r
+}\r
+\r
+\r
+/**\r
+ Get HTTP server response and collect the whole body as a file.\r
+ Set appropriate status in Context (REQ_OK, REQ_REPEAT, REQ_ERROR).\r
+ Note that even if HTTP server returns an error code, it might send\r
+ the body as well. This body will be collected in the resultant file.\r
+\r
+ @param[in] Context A pointer to the HTTP download context.\r
+ @param[in] DownloadUrl A pointer to the fully qualified URL to download.\r
+\r
+ @retval EFI_SUCCESS Valid file. Body successfully collected.\r
+ @retval EFI_HTTP_ERROR Response is a valid HTTP response, but the\r
+ HTTP server\r
+ indicated an error (HTTP code >= 400).\r
+ Response body MAY contain full\r
+ HTTP server response.\r
+ @retval Others Error getting the reponse from the HTTP server.\r
+ Response body is not collected.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetResponse (\r
+ IN HTTP_DOWNLOAD_CONTEXT *Context,\r
+ IN CHAR16 *DownloadUrl\r
+ )\r
+{\r
+ EFI_HTTP_RESPONSE_DATA ResponseData;\r
+ EFI_HTTP_MESSAGE ResponseMessage;\r
+ EFI_HTTP_HEADER *Header;\r
+ EFI_STATUS Status;\r
+ VOID *MsgParser;\r
+ EFI_TIME StartTime;\r
+ EFI_TIME EndTime;\r
+ CONST CHAR16 *Desc;\r
+ UINTN ElapsedSeconds;\r
+ BOOLEAN IsTrunked;\r
+ BOOLEAN CanMeasureTime;\r
+\r
+ ZeroMem (&ResponseData, sizeof (ResponseData));\r
+ ZeroMem (&ResponseMessage, sizeof (ResponseMessage));\r
+ ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));\r
+ IsTrunked = FALSE;\r
+\r
+ ResponseMessage.Body = Context->Buffer;\r
+ Context->ResponseToken.Status = EFI_SUCCESS;\r
+ Context->ResponseToken.Message = &ResponseMessage;\r
+ Context->ContentLength = 0;\r
+ Context->Status = REQ_OK;\r
+ MsgParser = NULL;\r
+ ResponseData.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;\r
+ ResponseMessage.Data.Response = &ResponseData;\r
+ Context->ResponseToken.Event = NULL;\r
+ CanMeasureTime = FALSE;\r
+ if (Context->Flags & DL_FLAG_TIME) {\r
+ ZeroMem (&StartTime, sizeof (StartTime));\r
+ CanMeasureTime = !EFI_ERROR (gRT->GetTime (&StartTime, NULL));\r
+ }\r
+\r
+ do {\r
+ SHELL_FREE_NON_NULL (ResponseMessage.Headers);\r
+ ResponseMessage.HeaderCount = 0;\r
+ gResponseCallbackComplete = FALSE;\r
+ ResponseMessage.BodyLength = Context->BufferSize;\r
+\r
+ if (ShellGetExecutionBreakFlag ()) {\r
+ Status = EFI_ABORTED;\r
+ break;\r
+ }\r
+\r
+ if (!Context->ContentDownloaded && !Context->ResponseToken.Event) {\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ ResponseCallback,\r
+ Context,\r
+ &Context->ResponseToken.Event\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ } else {\r
+ ResponseMessage.Data.Response = NULL;\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ Status = Context->Http->Response (Context->Http, &Context->ResponseToken);\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ Status = WaitForCompletion (Context, &gResponseCallbackComplete);\r
+ if (EFI_ERROR (Status) && ResponseMessage.HeaderCount) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Context->Http->Cancel (Context->Http, &Context->ResponseToken);\r
+ break;\r
+ }\r
+\r
+ if (!Context->ContentDownloaded) {\r
+ if (NEED_REDIRECTION (ResponseData.StatusCode)) {\r
+ //\r
+ // Need to repeat the request with new Location (server redirected).\r
+ //\r
+ Context->Status = REQ_NEED_REPEAT;\r
+\r
+ Header = HttpFindHeader (\r
+ ResponseMessage.HeaderCount,\r
+ ResponseMessage.Headers,\r
+ "Location"\r
+ );\r
+ if (Header) {\r
+ Status = SetHostURI (Header->FieldValue, Context, DownloadUrl);\r
+ if (Status == EFI_NO_MAPPING) {\r
+ PRINT_HII (\r
+ STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),\r
+ Context->ServerAddrAndProto,\r
+ L"Recursive HTTP server relocation",\r
+ Context->Uri\r
+ );\r
+ }\r
+ } else {\r
+ //\r
+ // Bad reply from the server. Server must specify the location.\r
+ // Indicate that resource was not found, and no body collected.\r
+ //\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+\r
+ Context->Http->Cancel (Context->Http, &Context->ResponseToken);\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Init message-body parser by header information.\r
+ //\r
+ if (!MsgParser) {\r
+ Status = HttpInitMsgParser (\r
+ ResponseMessage.Data.Request->Method,\r
+ ResponseData.StatusCode,\r
+ ResponseMessage.HeaderCount,\r
+ ResponseMessage.Headers,\r
+ ParseMsg,\r
+ Context,\r
+ &MsgParser\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If it is a trunked message, rely on the parser.\r
+ //\r
+ Header = HttpFindHeader (\r
+ ResponseMessage.HeaderCount,\r
+ ResponseMessage.Headers,\r
+ "Transfer-Encoding"\r
+ );\r
+ IsTrunked = (Header && !AsciiStrCmp (Header->FieldValue, "chunked"));\r
+\r
+ HttpGetEntityLength (MsgParser, &Context->ContentLength);\r
+\r
+ if (ResponseData.StatusCode >= HTTP_STATUS_400_BAD_REQUEST\r
+ && (ResponseData.StatusCode != HTTP_STATUS_308_PERMANENT_REDIRECT))\r
+ {\r
+ //\r
+ // Server reported an error via Response code.\r
+ // Collect the body if any.\r
+ //\r
+ if (!gHttpError) {\r
+ gHttpError = TRUE;\r
+\r
+ Desc = ErrStatusDesc[ResponseData.StatusCode -\r
+ HTTP_STATUS_400_BAD_REQUEST];\r
+ PRINT_HII (\r
+ STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),\r
+ Context->ServerAddrAndProto,\r
+ Desc,\r
+ Context->Uri\r
+ );\r
+\r
+ //\r
+ // This gives an RFC HTTP error.\r
+ //\r
+ Context->Status = ShellStrToUintn (Desc);\r
+ Status = ENCODE_ERROR (Context->Status);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Do NOT try to parse an empty body.\r
+ //\r
+ if (ResponseMessage.BodyLength || IsTrunked) {\r
+ Status = HttpParseMessageBody (\r
+ MsgParser,\r
+ ResponseMessage.BodyLength,\r
+ ResponseMessage.Body\r
+ );\r
+ }\r
+ } while (!HttpIsMessageComplete (MsgParser)\r
+ && !EFI_ERROR (Status)\r
+ && ResponseMessage.BodyLength);\r
+\r
+ if (Context->Status != REQ_NEED_REPEAT\r
+ && Status == EFI_SUCCESS\r
+ && CanMeasureTime)\r
+ {\r
+ if (!EFI_ERROR (gRT->GetTime (&EndTime, NULL))) {\r
+ ElapsedSeconds = EfiTimeToEpoch (&EndTime) - EfiTimeToEpoch (&StartTime);\r
+ Print (\r
+ L",%a%Lus\n",\r
+ ElapsedSeconds ? " " : " < ",\r
+ ElapsedSeconds > 1 ? (UINT64)ElapsedSeconds : 1\r
+ );\r
+ }\r
+ }\r
+\r
+ SHELL_FREE_NON_NULL (MsgParser);\r
+ if (Context->ResponseToken.Event) {\r
+ gBS->CloseEvent (Context->ResponseToken.Event);\r
+ ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Worker function that downloads the data of a file from an HTTP server given\r
+ the path of the file and its size.\r
+\r
+ @param[in] Context A pointer to the HTTP download context.\r
+ @param[in] ControllerHandle The handle of the network interface controller\r
+ @param[in] NicName NIC name\r
+\r
+ @retval EFI_SUCCESS The file was downloaded.\r
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
+ #return EFI_HTTP_ERROR The server returned a valid HTTP error.\r
+ Examine the mLocalFilePath file\r
+ to get error body.\r
+ @retval Others The downloading of the file from the server\r
+ failed.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DownloadFile (\r
+ IN HTTP_DOWNLOAD_CONTEXT *Context,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN CHAR16 *NicName\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *DownloadUrl;\r
+ UINTN UrlSize;\r
+ EFI_HANDLE HttpChildHandle;\r
+\r
+ ASSERT (Context);\r
+ if (Context == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DownloadUrl = NULL;\r
+ HttpChildHandle = NULL;\r
+\r
+ Context->Buffer = AllocatePool (Context->BufferSize);\r
+ if (Context->Buffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Open the file.\r
+ //\r
+ if (!EFI_ERROR (ShellFileExists (mLocalFilePath))) {\r
+ ShellDeleteFileByName (mLocalFilePath);\r
+ }\r
+\r
+ Status = ShellOpenFileByName (\r
+ mLocalFilePath,\r
+ &mFileHandle,\r
+ EFI_FILE_MODE_CREATE |\r
+ EFI_FILE_MODE_WRITE |\r
+ EFI_FILE_MODE_READ,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ PRINT_HII_APP (STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), mLocalFilePath);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ do {\r
+ SHELL_FREE_NON_NULL (DownloadUrl);\r
+\r
+ CLOSE_HTTP_HANDLE (ControllerHandle, HttpChildHandle);\r
+\r
+ Status = CreateServiceChildAndOpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiHttpServiceBindingProtocolGuid,\r
+ &gEfiHttpProtocolGuid,\r
+ &HttpChildHandle,\r
+ (VOID**)&Context->Http\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_OPEN_PROTOCOL), NicName, Status);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Context->Http->Configure (Context->Http, &Context->HttpConfigData);\r
+ if (EFI_ERROR (Status)) {\r
+ PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_CONFIGURE), NicName, Status);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ UrlSize = 0;\r
+ DownloadUrl = StrnCatGrow (\r
+ &DownloadUrl,\r
+ &UrlSize,\r
+ Context->ServerAddrAndProto,\r
+ StrLen (Context->ServerAddrAndProto)\r
+ );\r
+ if (Context->Uri[0] != L'/') {\r
+ DownloadUrl = StrnCatGrow (\r
+ &DownloadUrl,\r
+ &UrlSize,\r
+ L"/",\r
+ StrLen (Context->ServerAddrAndProto)\r
+ );\r
+ }\r
+\r
+ DownloadUrl = StrnCatGrow (\r
+ &DownloadUrl,\r
+ &UrlSize,\r
+ Context->Uri,\r
+ StrLen (Context->Uri));\r
+\r
+ PRINT_HII (STRING_TOKEN (STR_HTTP_DOWNLOADING), DownloadUrl);\r
+\r
+ Status = SendRequest (Context, DownloadUrl);\r
+ if (Status) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = GetResponse (Context, DownloadUrl);\r
+\r
+ if (Status) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ } while (Context->Status == REQ_NEED_REPEAT);\r
+\r
+ if (Context->Status) {\r
+ Status = ENCODE_ERROR (Context->Status);\r
+ }\r
+\r
+ON_EXIT:\r
+ //\r
+ // Close the file.\r
+ //\r
+ if (mFileHandle != NULL) {\r
+ if (EFI_ERROR (Status) && !(Context->Flags & DL_FLAG_KEEP_BAD)) {\r
+ ShellDeleteFile (&mFileHandle);\r
+ } else {\r
+ ShellCloseFile (&mFileHandle);\r
+ }\r
+ }\r
+\r
+ SHELL_FREE_NON_NULL (DownloadUrl);\r
+ SHELL_FREE_NON_NULL (Context->Buffer);\r
+\r
+ CLOSE_HTTP_HANDLE (ControllerHandle, HttpChildHandle);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Retrive HII package list from ImageHandle and publish to HII database.\r
+\r
+ @param[in] ImageHandle The image handle of the process.\r
+\r
+ @retval HII handle.\r
+**/\r
+EFI_HII_HANDLE\r
+InitializeHiiPackage (\r
+ IN EFI_HANDLE ImageHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;\r
+ EFI_HII_HANDLE HiiHandle;\r
+\r
+ //\r
+ // Retrieve HII package list from ImageHandle.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ImageHandle,\r
+ &gEfiHiiPackageListProtocolGuid,\r
+ (VOID **)&PackageList,\r
+ ImageHandle,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Publish HII package list to HII Database.\r
+ //\r
+ Status = gHiiDatabase->NewPackageList (\r
+ gHiiDatabase,\r
+ PackageList,\r
+ NULL,\r
+ &HiiHandle\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+\r
+ return HiiHandle;\r
+}\r