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