]> git.proxmox.com Git - mirror_edk2.git/blame - ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c
ShellPkg: Apply uncrustify changes
[mirror_edk2.git] / ShellPkg / DynamicCommand / HttpDynamicCommand / Http.c
CommitLineData
d8ab884f
VO
1/** @file\r
2 The implementation for the 'http' Shell command.\r
3\r
4 Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>\r
5 Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved. <BR>\r
6 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
7 Copyright (c) 2020, Broadcom. All rights reserved. <BR>\r
8\r
9 SPDX-License-Identifier: BSD-2-Clause-Patent\r
10**/\r
11\r
12#include "Http.h"\r
13\r
47d20b54 14#define IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH 32\r
d8ab884f
VO
15\r
16//\r
17// Constant strings and definitions related to the message\r
18// indicating the amount of progress in the dowloading of a HTTP file.\r
19//\r
20\r
21//\r
22// Number of steps in the progression slider.\r
23//\r
24#define HTTP_PROGRESS_SLIDER_STEPS \\r
25 ((sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) - 3)\r
26\r
27//\r
28// Size in number of characters plus one (final zero) of the message to\r
29// indicate the progress of an HTTP download. The format is "[(progress slider:\r
30// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There\r
31// are thus the number of characters in HTTP_PROGR_FRAME[] plus 11 characters\r
32// (2 // spaces, "Kb" and seven characters for the number of KBytes).\r
33//\r
34#define HTTP_PROGRESS_MESSAGE_SIZE \\r
35 ((sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) + 12)\r
36\r
37//\r
38// Buffer size. Note that larger buffer does not mean better speed.\r
39//\r
47d20b54
MK
40#define DEFAULT_BUF_SIZE SIZE_32KB\r
41#define MAX_BUF_SIZE SIZE_4MB\r
d8ab884f 42\r
47d20b54
MK
43#define MIN_PARAM_COUNT 2\r
44#define MAX_PARAM_COUNT 4\r
d8ab884f
VO
45#define NEED_REDIRECTION(Code) \\r
46 (((Code >= HTTP_STATUS_300_MULTIPLE_CHOICES) \\r
47 && (Code <= HTTP_STATUS_307_TEMPORARY_REDIRECT)) \\r
48 || (Code == HTTP_STATUS_308_PERMANENT_REDIRECT))\r
49\r
47d20b54 50#define CLOSE_HTTP_HANDLE(ControllerHandle, HttpChildHandle) \\r
d8ab884f
VO
51 do { \\r
52 if (HttpChildHandle) { \\r
53 CloseProtocolAndDestroyServiceChild ( \\r
54 ControllerHandle, \\r
55 &gEfiHttpServiceBindingProtocolGuid, \\r
56 &gEfiHttpProtocolGuid, \\r
57 HttpChildHandle \\r
58 ); \\r
59 HttpChildHandle = NULL; \\r
60 } \\r
61 } while (0)\r
62\r
63typedef enum {\r
64 HdrHost,\r
65 HdrConn,\r
66 HdrAgent,\r
67 HdrMax\r
68} HDR_TYPE;\r
69\r
70#define USER_AGENT_HDR "Mozilla/5.0 (EDK2; Linux) Gecko/20100101 Firefox/79.0"\r
71\r
47d20b54 72#define TIMER_MAX_TIMEOUT_S 10\r
d8ab884f
VO
73\r
74//\r
75// File name to use when Uri ends with "/".\r
76//\r
47d20b54
MK
77#define DEFAULT_HTML_FILE L"index.html"\r
78#define DEFAULT_HTTP_PROTO L"http"\r
d8ab884f
VO
79\r
80//\r
81// String to delete the HTTP progress message to be able to update it :\r
82// (HTTP_PROGRESS_MESSAGE_SIZE-1) '\b'.\r
83//\r
84#define HTTP_PROGRESS_DEL \\r
85 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
86\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"\r
87\r
47d20b54 88#define HTTP_KB L"\b\b\b\b\b\b\b\b\b\b"\r
d8ab884f
VO
89//\r
90// Frame for the progression slider.\r
91//\r
47d20b54 92#define HTTP_PROGR_FRAME L"[ ]"\r
d8ab884f
VO
93\r
94//\r
95// Improve readability by using these macros.\r
96//\r
47d20b54 97#define PRINT_HII(token, ...) \\r
d8ab884f
VO
98 ShellPrintHiiEx (\\r
99 -1, -1, NULL, token, mHttpHiiHandle, __VA_ARGS__)\r
100\r
47d20b54 101#define PRINT_HII_APP(token, value) \\r
d8ab884f
VO
102 PRINT_HII (token, HTTP_APP_NAME, value)\r
103\r
104//\r
105// TimeBaseLib.h constants.\r
106// These will be removed once the library gets fixed.\r
107//\r
108\r
109//\r
110// Define EPOCH (1970-JANUARY-01) in the Julian Date representation.\r
111//\r
47d20b54 112#define EPOCH_JULIAN_DATE 2440588\r
d8ab884f
VO
113\r
114//\r
115// Seconds per unit.\r
116//\r
47d20b54
MK
117#define SEC_PER_MIN ((UINTN) 60)\r
118#define SEC_PER_HOUR ((UINTN) 3600)\r
119#define SEC_PER_DAY ((UINTN) 86400)\r
d8ab884f
VO
120\r
121//\r
122// String descriptions for server errors.\r
123//\r
47d20b54 124STATIC CONST CHAR16 *ErrStatusDesc[] =\r
d8ab884f
VO
125{\r
126 L"400 Bad Request",\r
127 L"401 Unauthorized",\r
128 L"402 Payment required",\r
129 L"403 Forbidden",\r
130 L"404 Not Found",\r
131 L"405 Method not allowed",\r
132 L"406 Not acceptable",\r
133 L"407 Proxy authentication required",\r
134 L"408 Request time out",\r
135 L"409 Conflict",\r
136 L"410 Gone",\r
137 L"411 Length required",\r
138 L"412 Precondition failed",\r
139 L"413 Request entity too large",\r
140 L"414 Request URI to large",\r
141 L"415 Unsupported media type",\r
142 L"416 Requested range not satisfied",\r
143 L"417 Expectation failed",\r
144 L"500 Internal server error",\r
145 L"501 Not implemented",\r
146 L"502 Bad gateway",\r
147 L"503 Service unavailable",\r
148 L"504 Gateway timeout",\r
149 L"505 HTTP version not supported"\r
150};\r
151\r
47d20b54
MK
152STATIC CONST SHELL_PARAM_ITEM ParamList[] = {\r
153 { L"-i", TypeValue },\r
154 { L"-k", TypeFlag },\r
155 { L"-l", TypeValue },\r
156 { L"-m", TypeFlag },\r
157 { L"-s", TypeValue },\r
158 { L"-t", TypeValue },\r
159 { NULL, TypeMax }\r
d8ab884f
VO
160};\r
161\r
162//\r
163// Local File Handle.\r
164//\r
47d20b54 165STATIC SHELL_FILE_HANDLE mFileHandle = NULL;\r
d8ab884f
VO
166\r
167//\r
168// Path of the local file, Unicode encoded.\r
169//\r
47d20b54 170STATIC CONST CHAR16 *mLocalFilePath;\r
d8ab884f 171\r
47d20b54
MK
172STATIC BOOLEAN gRequestCallbackComplete = FALSE;\r
173STATIC BOOLEAN gResponseCallbackComplete = FALSE;\r
d8ab884f 174\r
47d20b54 175STATIC BOOLEAN gHttpError;\r
d8ab884f 176\r
47d20b54 177EFI_HII_HANDLE mHttpHiiHandle;\r
d8ab884f
VO
178\r
179//\r
180// Functions declarations.\r
181//\r
182\r
183/**\r
184 Check and convert the UINT16 option values of the 'http' command.\r
185\r
186 @param[in] ValueStr Value as an Unicode encoded string.\r
187 @param[out] Value UINT16 value.\r
188\r
189 @retval TRUE The value was returned.\r
190 @retval FALSE A parsing error occured.\r
191**/\r
192STATIC\r
193BOOLEAN\r
194StringToUint16 (\r
195 IN CONST CHAR16 *ValueStr,\r
196 OUT UINT16 *Value\r
197 );\r
198\r
199/**\r
200 Get the name of the NIC.\r
201\r
202 @param[in] ControllerHandle The network physical device handle.\r
203 @param[in] NicNumber The network physical device number.\r
204 @param[out] NicName Address where to store the NIC name.\r
205 The memory area has to be at least\r
206 IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH\r
207 double byte wide.\r
208\r
209 @retval EFI_SUCCESS The name of the NIC was returned.\r
210 @retval Others The creation of the child for the Managed\r
211 Network Service failed or the opening of\r
212 the Managed Network Protocol failed or\r
213 the operational parameters for the\r
214 Managed Network Protocol could not be\r
215 read.\r
216**/\r
217STATIC\r
218EFI_STATUS\r
219GetNicName (\r
220 IN EFI_HANDLE ControllerHandle,\r
221 IN UINTN NicNumber,\r
222 OUT CHAR16 *NicName\r
223 );\r
224\r
225/**\r
226 Create a child for the service identified by its service binding protocol GUID\r
227 and get from the child the interface of the protocol identified by its GUID.\r
228\r
229 @param[in] ControllerHandle Controller handle.\r
230 @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
231 service to be created.\r
232 @param[in] ProtocolGuid GUID of the protocol to be open.\r
233 @param[out] ChildHandle Address where the handler of the\r
234 created child is returned. NULL is\r
235 returned in case of error.\r
236 @param[out] Interface Address where a pointer to the\r
237 protocol interface is returned in\r
238 case of success.\r
239\r
240 @retval EFI_SUCCESS The child was created and the protocol opened.\r
241 @retval Others Either the creation of the child or the opening\r
242 of the protocol failed.\r
243**/\r
244STATIC\r
245EFI_STATUS\r
246CreateServiceChildAndOpenProtocol (\r
247 IN EFI_HANDLE ControllerHandle,\r
248 IN EFI_GUID *ServiceBindingProtocolGuid,\r
249 IN EFI_GUID *ProtocolGuid,\r
250 OUT EFI_HANDLE *ChildHandle,\r
251 OUT VOID **Interface\r
252 );\r
253\r
254/**\r
255 Close the protocol identified by its GUID on the child handle of the service\r
256 identified by its service binding protocol GUID, then destroy the child\r
257 handle.\r
258\r
259 @param[in] ControllerHandle Controller handle.\r
260 @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
261 service to be destroyed.\r
262 @param[in] ProtocolGuid GUID of the protocol to be closed.\r
263 @param[in] ChildHandle Handle of the child to be destroyed.\r
264\r
265**/\r
266STATIC\r
267VOID\r
268CloseProtocolAndDestroyServiceChild (\r
269 IN EFI_HANDLE ControllerHandle,\r
270 IN EFI_GUID *ServiceBindingProtocolGuid,\r
271 IN EFI_GUID *ProtocolGuid,\r
272 IN EFI_HANDLE ChildHandle\r
273 );\r
274\r
275/**\r
276 Worker function that download the data of a file from an HTTP server given\r
277 the path of the file and its size.\r
278\r
279 @param[in] Context A pointer to the download context.\r
280\r
281 @retval EFI_SUCCESS The file was downloaded.\r
282 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
283 @retval Others The downloading of the file\r
284 from the server failed.\r
285**/\r
286STATIC\r
287EFI_STATUS\r
288DownloadFile (\r
47d20b54
MK
289 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
290 IN EFI_HANDLE ControllerHandle,\r
291 IN CHAR16 *NicName\r
d8ab884f
VO
292 );\r
293\r
294/**\r
295 Cleans off leading and trailing spaces and tabs.\r
296\r
297 @param[in] String pointer to the string to trim them off.\r
298\r
299 @retval EFI_SUCCESS No errors.\r
300 @retval EFI_INVALID_PARAMETER String pointer is NULL.\r
301**/\r
302STATIC\r
303EFI_STATUS\r
304TrimSpaces (\r
47d20b54 305 IN CHAR16 *String\r
d8ab884f
VO
306 )\r
307{\r
47d20b54
MK
308 CHAR16 *Str;\r
309 UINTN Len;\r
d8ab884f
VO
310\r
311 ASSERT (String != NULL);\r
312\r
313 if (String == NULL) {\r
314 return EFI_INVALID_PARAMETER;\r
315 }\r
316\r
317 Str = String;\r
318\r
319 //\r
320 // Remove any whitespace at the beginning of the Str.\r
321 //\r
322 while (*Str == L' ' || *Str == L'\t') {\r
323 Str++;\r
324 }\r
325\r
326 //\r
327 // Remove any whitespace at the end of the Str.\r
328 //\r
329 do {\r
330 Len = StrLen (Str);\r
47d20b54 331 if (!Len || ((Str[Len - 1] != L' ') && (Str[Len - 1] != '\t'))) {\r
d8ab884f
VO
332 break;\r
333 }\r
334\r
335 Str[Len - 1] = CHAR_NULL;\r
336 } while (Len);\r
337\r
338 CopyMem (String, Str, StrSize (Str));\r
339\r
340 return EFI_SUCCESS;\r
341}\r
342\r
343//\r
344// Callbacks for request and response.\r
345// We just acknowledge that operation has completed here.\r
346//\r
347\r
348/**\r
349 Callback to set the request completion flag.\r
350\r
351 @param[in] Event: The event.\r
352 @param[in] Context: pointer to Notification Context.\r
353 **/\r
354STATIC\r
355VOID\r
356EFIAPI\r
357RequestCallback (\r
47d20b54
MK
358 IN EFI_EVENT Event,\r
359 IN VOID *Context\r
d8ab884f
VO
360 )\r
361{\r
362 gRequestCallbackComplete = TRUE;\r
363}\r
364\r
365/**\r
366 Callback to set the response completion flag.\r
367 @param[in] Event: The event.\r
368 @param[in] Context: pointer to Notification Context.\r
369 **/\r
370STATIC\r
371VOID\r
372EFIAPI\r
373ResponseCallback (\r
47d20b54
MK
374 IN EFI_EVENT Event,\r
375 IN VOID *Context\r
d8ab884f
VO
376 )\r
377{\r
378 gResponseCallbackComplete = TRUE;\r
379}\r
380\r
381//\r
382// Set of functions from TimeBaseLib.\r
383// This will be removed once TimeBaseLib is enabled for ShellPkg.\r
384//\r
385\r
386/**\r
387 Calculate Epoch days.\r
388\r
389 @param[in] Time - a pointer to the EFI_TIME abstraction.\r
390\r
391 @retval Number of days elapsed since EPOCH_JULIAN_DAY.\r
392 **/\r
393STATIC\r
394UINTN\r
395EfiGetEpochDays (\r
396 IN EFI_TIME *Time\r
397 )\r
398{\r
47d20b54
MK
399 UINTN a;\r
400 UINTN y;\r
401 UINTN m;\r
d8ab884f
VO
402 //\r
403 // Absolute Julian Date representation of the supplied Time.\r
404 //\r
47d20b54 405 UINTN JulianDate;\r
d8ab884f
VO
406 //\r
407 // Number of days elapsed since EPOCH_JULIAN_DAY.\r
408 //\r
47d20b54 409 UINTN EpochDays;\r
d8ab884f 410\r
47d20b54 411 a = (14 - Time->Month) / 12;\r
d8ab884f
VO
412 y = Time->Year + 4800 - a;\r
413 m = Time->Month + (12 * a) - 3;\r
414\r
415 JulianDate = Time->Day + ((153 * m + 2) / 5) + (365 * y) + (y / 4) -\r
416 (y / 100) + (y / 400) - 32045;\r
417\r
418 ASSERT (JulianDate >= EPOCH_JULIAN_DATE);\r
419 EpochDays = JulianDate - EPOCH_JULIAN_DATE;\r
420\r
421 return EpochDays;\r
422}\r
423\r
424/**\r
425 Converts EFI_TIME to Epoch seconds\r
426 (elapsed since 1970 JANUARY 01, 00:00:00 UTC).\r
427\r
428 @param[in] Time: a pointer to EFI_TIME abstraction.\r
429 **/\r
430STATIC\r
431UINTN\r
432EFIAPI\r
433EfiTimeToEpoch (\r
434 IN EFI_TIME *Time\r
435 )\r
436{\r
437 //\r
438 // Number of days elapsed since EPOCH_JULIAN_DAY.\r
439 //\r
47d20b54
MK
440 UINTN EpochDays;\r
441 UINTN EpochSeconds;\r
d8ab884f
VO
442\r
443 EpochDays = EfiGetEpochDays (Time);\r
444\r
445 EpochSeconds = (EpochDays * SEC_PER_DAY) +\r
446 ((UINTN)Time->Hour * SEC_PER_HOUR) +\r
447 (Time->Minute * SEC_PER_MIN) + Time->Second;\r
448\r
449 return EpochSeconds;\r
450}\r
451\r
452/**\r
453 Function for 'http' command.\r
454\r
455 @param[in] ImageHandle Handle to the Image (NULL if Internal).\r
456 @param[in] SystemTable Pointer to the System Table (NULL if Internal).\r
457\r
458 @retval SHELL_SUCCESS The 'http' command completed successfully.\r
459 @retval SHELL_ABORTED The Shell Library initialization failed.\r
460 @retval SHELL_INVALID_PARAMETER At least one of the command's arguments is\r
461 not valid.\r
462 @retval SHELL_OUT_OF_RESOURCES A memory allocation failed.\r
463 @retval SHELL_NOT_FOUND Network Interface Card not found.\r
464 @retval SHELL_UNSUPPORTED Command was valid, but the server returned\r
465 a status code indicating some error.\r
466 Examine the file requested for error body.\r
467**/\r
468SHELL_STATUS\r
469RunHttp (\r
470 IN EFI_HANDLE ImageHandle,\r
471 IN EFI_SYSTEM_TABLE *SystemTable\r
472 )\r
473{\r
47d20b54
MK
474 EFI_STATUS Status;\r
475 LIST_ENTRY *CheckPackage;\r
476 UINTN ParamCount;\r
477 UINTN HandleCount;\r
478 UINTN NicNumber;\r
479 UINTN InitialSize;\r
480 UINTN ParamOffset;\r
481 UINTN StartSize;\r
482 CHAR16 *ProblemParam;\r
483 CHAR16 NicName[IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH];\r
484 CHAR16 *Walker1;\r
485 CHAR16 *VStr;\r
486 CONST CHAR16 *UserNicName;\r
487 CONST CHAR16 *ValueStr;\r
488 CONST CHAR16 *RemoteFilePath;\r
489 CONST CHAR16 *Walker;\r
490 EFI_HTTPv4_ACCESS_POINT IPv4Node;\r
491 EFI_HANDLE *Handles;\r
492 EFI_HANDLE ControllerHandle;\r
493 HTTP_DOWNLOAD_CONTEXT Context;\r
494 BOOLEAN NicFound;\r
495\r
496 ProblemParam = NULL;\r
497 RemoteFilePath = NULL;\r
498 NicFound = FALSE;\r
499 Handles = NULL;\r
d8ab884f
VO
500\r
501 //\r
502 // Initialize the Shell library (we must be in non-auto-init...).\r
503 //\r
504 ParamOffset = 0;\r
505 gHttpError = FALSE;\r
506\r
507 Status = ShellInitialize ();\r
508 if (EFI_ERROR (Status)) {\r
509 ASSERT_EFI_ERROR (Status);\r
510 return SHELL_ABORTED;\r
511 }\r
512\r
513 ZeroMem (&Context, sizeof (Context));\r
514\r
515 //\r
516 // Parse the command line.\r
517 //\r
518 Status = ShellCommandLineParse (\r
519 ParamList,\r
520 &CheckPackage,\r
521 &ProblemParam,\r
522 TRUE\r
523 );\r
524 if (EFI_ERROR (Status)) {\r
47d20b54
MK
525 if ( (Status == EFI_VOLUME_CORRUPTED)\r
526 && (ProblemParam != NULL))\r
d8ab884f
VO
527 {\r
528 PRINT_HII_APP (STRING_TOKEN (STR_GEN_PROBLEM), ProblemParam);\r
529 SHELL_FREE_NON_NULL (ProblemParam);\r
530 } else {\r
531 ASSERT (FALSE);\r
532 }\r
533\r
534 goto Error;\r
535 }\r
536\r
537 //\r
538 // Check the number of parameters.\r
539 //\r
540 Status = EFI_INVALID_PARAMETER;\r
541\r
542 ParamCount = ShellCommandLineGetCount (CheckPackage);\r
543 if (ParamCount > MAX_PARAM_COUNT) {\r
544 PRINT_HII_APP (STRING_TOKEN (STR_GEN_TOO_MANY), NULL);\r
545 goto Error;\r
546 }\r
547\r
548 if (ParamCount < MIN_PARAM_COUNT) {\r
549 PRINT_HII_APP (STRING_TOKEN (STR_GEN_TOO_FEW), NULL);\r
550 goto Error;\r
551 }\r
552\r
553 ZeroMem (&Context.HttpConfigData, sizeof (Context.HttpConfigData));\r
554 ZeroMem (&IPv4Node, sizeof (IPv4Node));\r
555 IPv4Node.UseDefaultAddress = TRUE;\r
556\r
47d20b54 557 Context.HttpConfigData.HttpVersion = HttpVersion11;\r
d8ab884f
VO
558 Context.HttpConfigData.AccessPoint.IPv4Node = &IPv4Node;\r
559\r
560 //\r
561 // Get the host address (not necessarily IPv4 format).\r
562 //\r
563 ValueStr = ShellCommandLineGetRawValue (CheckPackage, 1);\r
564 if (!ValueStr) {\r
565 PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
566 goto Error;\r
567 } else {\r
568 StartSize = 0;\r
569 TrimSpaces ((CHAR16 *)ValueStr);\r
570 if (!StrStr (ValueStr, L"://")) {\r
571 Context.ServerAddrAndProto = StrnCatGrow (\r
572 &Context.ServerAddrAndProto,\r
573 &StartSize,\r
574 DEFAULT_HTTP_PROTO,\r
575 StrLen (DEFAULT_HTTP_PROTO)\r
576 );\r
577 Context.ServerAddrAndProto = StrnCatGrow (\r
578 &Context.ServerAddrAndProto,\r
579 &StartSize,\r
580 L"://",\r
581 StrLen (L"://")\r
582 );\r
583 VStr = (CHAR16 *)ValueStr;\r
584 } else {\r
585 VStr = StrStr (ValueStr, L"://") + StrLen (L"://");\r
586 }\r
587\r
588 for (Walker1 = VStr; *Walker1; Walker1++) {\r
589 if (*Walker1 == L'/') {\r
590 break;\r
591 }\r
592 }\r
593\r
594 if (*Walker1 == L'/') {\r
47d20b54 595 ParamOffset = 1;\r
d8ab884f
VO
596 RemoteFilePath = Walker1;\r
597 }\r
598\r
599 Context.ServerAddrAndProto = StrnCatGrow (\r
600 &Context.ServerAddrAndProto,\r
601 &StartSize,\r
602 ValueStr,\r
603 StrLen (ValueStr) - StrLen (Walker1)\r
604 );\r
605 if (!Context.ServerAddrAndProto) {\r
606 Status = EFI_OUT_OF_RESOURCES;\r
607 goto Error;\r
608 }\r
609 }\r
610\r
611 if (!RemoteFilePath) {\r
612 RemoteFilePath = ShellCommandLineGetRawValue (CheckPackage, 2);\r
613 if (!RemoteFilePath) {\r
614 //\r
615 // If no path given, assume just "/".\r
616 //\r
617 RemoteFilePath = L"/";\r
618 }\r
619 }\r
620\r
621 TrimSpaces ((CHAR16 *)RemoteFilePath);\r
622\r
623 if (ParamCount == MAX_PARAM_COUNT - ParamOffset) {\r
624 mLocalFilePath = ShellCommandLineGetRawValue (\r
625 CheckPackage,\r
626 MAX_PARAM_COUNT - 1 - ParamOffset\r
627 );\r
628 } else {\r
629 Walker = RemoteFilePath + StrLen (RemoteFilePath);\r
630 while ((--Walker) >= RemoteFilePath) {\r
631 if ((*Walker == L'\\') ||\r
47d20b54
MK
632 (*Walker == L'/'))\r
633 {\r
d8ab884f
VO
634 break;\r
635 }\r
636 }\r
637\r
638 mLocalFilePath = Walker + 1;\r
639 }\r
640\r
641 if (!StrLen (mLocalFilePath)) {\r
642 mLocalFilePath = DEFAULT_HTML_FILE;\r
643 }\r
644\r
645 InitialSize = 0;\r
646 Context.Uri = StrnCatGrow (\r
647 &Context.Uri,\r
648 &InitialSize,\r
649 RemoteFilePath,\r
650 StrLen (RemoteFilePath)\r
651 );\r
652 if (!Context.Uri) {\r
653 Status = EFI_OUT_OF_RESOURCES;\r
654 goto Error;\r
655 }\r
656\r
657 //\r
658 // Get the name of the Network Interface Card to be used if any.\r
659 //\r
660 UserNicName = ShellCommandLineGetValue (CheckPackage, L"-i");\r
661\r
662 ValueStr = ShellCommandLineGetValue (CheckPackage, L"-l");\r
47d20b54
MK
663 if ( (ValueStr != NULL)\r
664 && (!StringToUint16 (\r
665 ValueStr,\r
666 &Context.HttpConfigData.AccessPoint.IPv4Node->LocalPort\r
667 )\r
668 ))\r
d8ab884f
VO
669 {\r
670 goto Error;\r
671 }\r
672\r
673 Context.BufferSize = DEFAULT_BUF_SIZE;\r
674\r
675 ValueStr = ShellCommandLineGetValue (CheckPackage, L"-s");\r
676 if (ValueStr != NULL) {\r
677 Context.BufferSize = ShellStrToUintn (ValueStr);\r
47d20b54 678 if (!Context.BufferSize || (Context.BufferSize > MAX_BUF_SIZE)) {\r
d8ab884f
VO
679 PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
680 goto Error;\r
681 }\r
682 }\r
683\r
684 ValueStr = ShellCommandLineGetValue (CheckPackage, L"-t");\r
685 if (ValueStr != NULL) {\r
686 Context.HttpConfigData.TimeOutMillisec = (UINT32)ShellStrToUintn (ValueStr);\r
687 }\r
688\r
689 //\r
690 // Locate all HTTP Service Binding protocols.\r
691 //\r
692 Status = gBS->LocateHandleBuffer (\r
693 ByProtocol,\r
694 &gEfiManagedNetworkServiceBindingProtocolGuid,\r
695 NULL,\r
696 &HandleCount,\r
697 &Handles\r
698 );\r
699 if (EFI_ERROR (Status) || (HandleCount == 0)) {\r
700 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NO_NIC), NULL);\r
701 if (!EFI_ERROR (Status)) {\r
702 Status = EFI_NOT_FOUND;\r
703 }\r
704\r
705 goto Error;\r
706 }\r
707\r
708 Status = EFI_NOT_FOUND;\r
709\r
710 Context.Flags = 0;\r
711 if (ShellCommandLineGetFlag (CheckPackage, L"-m")) {\r
712 Context.Flags |= DL_FLAG_TIME;\r
713 }\r
714\r
715 if (ShellCommandLineGetFlag (CheckPackage, L"-k")) {\r
716 Context.Flags |= DL_FLAG_KEEP_BAD;\r
717 }\r
718\r
719 for (NicNumber = 0;\r
720 (NicNumber < HandleCount) && (Status != EFI_SUCCESS);\r
721 NicNumber++)\r
722 {\r
723 ControllerHandle = Handles[NicNumber];\r
724\r
725 Status = GetNicName (ControllerHandle, NicNumber, NicName);\r
726 if (EFI_ERROR (Status)) {\r
727 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NIC_NAME), NicNumber, Status);\r
728 continue;\r
729 }\r
730\r
731 if (UserNicName != NULL) {\r
732 if (StrCmp (NicName, UserNicName) != 0) {\r
733 Status = EFI_NOT_FOUND;\r
734 continue;\r
735 }\r
736\r
737 NicFound = TRUE;\r
738 }\r
739\r
740 Status = DownloadFile (&Context, ControllerHandle, NicName);\r
741 PRINT_HII (STRING_TOKEN (STR_GEN_CRLF), NULL);\r
742\r
743 if (EFI_ERROR (Status)) {\r
744 PRINT_HII (\r
745 STRING_TOKEN (STR_HTTP_ERR_DOWNLOAD),\r
746 RemoteFilePath,\r
747 NicName,\r
748 Status\r
749 );\r
750 //\r
751 // If a user aborted the operation,\r
752 // do not try another controller.\r
753 //\r
754 if (Status == EFI_ABORTED) {\r
755 goto Error;\r
756 }\r
757 }\r
758\r
759 if (gHttpError) {\r
47d20b54
MK
760 //\r
761 // This is not related to connection, so no need to repeat with\r
762 // another interface.\r
763 //\r
d8ab884f
VO
764 break;\r
765 }\r
766 }\r
767\r
768 if ((UserNicName != NULL) && (!NicFound)) {\r
769 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NIC_NOT_FOUND), UserNicName);\r
770 }\r
771\r
772Error:\r
773 ShellCommandLineFreeVarList (CheckPackage);\r
774 SHELL_FREE_NON_NULL (Handles);\r
775 SHELL_FREE_NON_NULL (Context.ServerAddrAndProto);\r
776 SHELL_FREE_NON_NULL (Context.Uri);\r
777\r
778 return Status & ~MAX_BIT;\r
779}\r
780\r
781/**\r
782 Check and convert the UINT16 option values of the 'http' command\r
783\r
784 @param[in] ValueStr Value as an Unicode encoded string\r
785 @param[out] Value UINT16 value\r
786\r
787 @retval TRUE The value was returned.\r
788 @retval FALSE A parsing error occured.\r
789**/\r
790STATIC\r
791BOOLEAN\r
792StringToUint16 (\r
793 IN CONST CHAR16 *ValueStr,\r
794 OUT UINT16 *Value\r
795 )\r
796{\r
797 UINTN Val;\r
798\r
799 Val = ShellStrToUintn (ValueStr);\r
800 if (Val > MAX_UINT16) {\r
801 PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
802 return FALSE;\r
803 }\r
804\r
805 *Value = (UINT16)Val;\r
806 return TRUE;\r
807}\r
808\r
809/**\r
810 Get the name of the NIC.\r
811\r
812 @param[in] ControllerHandle The network physical device handle.\r
813 @param[in] NicNumber The network physical device number.\r
814 @param[out] NicName Address where to store the NIC name.\r
815 The memory area has to be at least\r
816 IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH\r
817 double byte wide.\r
818\r
819 @retval EFI_SUCCESS The name of the NIC was returned.\r
820 @retval Others The creation of the child for the Managed\r
821 Network Service failed or the opening of\r
822 the Managed Network Protocol failed or\r
823 the operational parameters for the\r
824 Managed Network Protocol could not be\r
825 read.\r
826**/\r
827STATIC\r
828EFI_STATUS\r
829GetNicName (\r
830 IN EFI_HANDLE ControllerHandle,\r
831 IN UINTN NicNumber,\r
832 OUT CHAR16 *NicName\r
833 )\r
834{\r
835 EFI_STATUS Status;\r
836 EFI_HANDLE MnpHandle;\r
837 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
838 EFI_SIMPLE_NETWORK_MODE SnpMode;\r
839\r
840 Status = CreateServiceChildAndOpenProtocol (\r
841 ControllerHandle,\r
842 &gEfiManagedNetworkServiceBindingProtocolGuid,\r
843 &gEfiManagedNetworkProtocolGuid,\r
844 &MnpHandle,\r
47d20b54 845 (VOID **)&Mnp\r
d8ab884f
VO
846 );\r
847 if (EFI_ERROR (Status)) {\r
848 goto Error;\r
849 }\r
850\r
851 Status = Mnp->GetModeData (Mnp, NULL, &SnpMode);\r
852 if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {\r
853 goto Error;\r
854 }\r
855\r
856 UnicodeSPrint (\r
857 NicName,\r
858 IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH,\r
859 SnpMode.IfType == NET_IFTYPE_ETHERNET ? L"eth%d" : L"unk%d",\r
860 NicNumber\r
861 );\r
862\r
863 Status = EFI_SUCCESS;\r
864\r
865Error:\r
866\r
867 if (MnpHandle != NULL) {\r
868 CloseProtocolAndDestroyServiceChild (\r
869 ControllerHandle,\r
870 &gEfiManagedNetworkServiceBindingProtocolGuid,\r
871 &gEfiManagedNetworkProtocolGuid,\r
872 MnpHandle\r
873 );\r
874 }\r
875\r
876 return Status;\r
877}\r
878\r
879/**\r
880 Create a child for the service identified by its service binding protocol GUID\r
881 and get from the child the interface of the protocol identified by its GUID.\r
882\r
883 @param[in] ControllerHandle Controller handle.\r
884 @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
885 service to be created.\r
886 @param[in] ProtocolGuid GUID of the protocol to be open.\r
887 @param[out] ChildHandle Address where the handler of the\r
888 created child is returned. NULL is\r
889 returned in case of error.\r
890 @param[out] Interface Address where a pointer to the\r
891 protocol interface is returned in\r
892 case of success.\r
893\r
894 @retval EFI_SUCCESS The child was created and the protocol opened.\r
895 @retval Others Either the creation of the child or the opening\r
896 of the protocol failed.\r
897**/\r
898STATIC\r
899EFI_STATUS\r
900CreateServiceChildAndOpenProtocol (\r
901 IN EFI_HANDLE ControllerHandle,\r
902 IN EFI_GUID *ServiceBindingProtocolGuid,\r
903 IN EFI_GUID *ProtocolGuid,\r
904 OUT EFI_HANDLE *ChildHandle,\r
905 OUT VOID **Interface\r
906 )\r
907{\r
908 EFI_STATUS Status;\r
909\r
910 *ChildHandle = NULL;\r
47d20b54
MK
911 Status = NetLibCreateServiceChild (\r
912 ControllerHandle,\r
913 gImageHandle,\r
914 ServiceBindingProtocolGuid,\r
915 ChildHandle\r
916 );\r
d8ab884f
VO
917 if (!EFI_ERROR (Status)) {\r
918 Status = gBS->OpenProtocol (\r
919 *ChildHandle,\r
920 ProtocolGuid,\r
921 Interface,\r
922 gImageHandle,\r
923 ControllerHandle,\r
924 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
925 );\r
926 if (EFI_ERROR (Status)) {\r
927 NetLibDestroyServiceChild (\r
928 ControllerHandle,\r
929 gImageHandle,\r
930 ServiceBindingProtocolGuid,\r
931 *ChildHandle\r
932 );\r
933 *ChildHandle = NULL;\r
934 }\r
935 }\r
936\r
937 return Status;\r
938}\r
939\r
940/**\r
941 Close the protocol identified by its GUID on the child handle of the service\r
942 identified by its service binding protocol GUID, then destroy the child\r
943 handle.\r
944\r
945 @param[in] ControllerHandle Controller handle.\r
946 @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
947 service to be destroyed.\r
948 @param[in] ProtocolGuid GUID of the protocol to be closed.\r
949 @param[in] ChildHandle Handle of the child to be destroyed.\r
950**/\r
951STATIC\r
952VOID\r
953CloseProtocolAndDestroyServiceChild (\r
954 IN EFI_HANDLE ControllerHandle,\r
955 IN EFI_GUID *ServiceBindingProtocolGuid,\r
956 IN EFI_GUID *ProtocolGuid,\r
957 IN EFI_HANDLE ChildHandle\r
958 )\r
959{\r
960 gBS->CloseProtocol (\r
961 ChildHandle,\r
962 ProtocolGuid,\r
963 gImageHandle,\r
964 ControllerHandle\r
965 );\r
966\r
967 NetLibDestroyServiceChild (\r
968 ControllerHandle,\r
969 gImageHandle,\r
970 ServiceBindingProtocolGuid,\r
971 ChildHandle\r
972 );\r
973}\r
974\r
975/**\r
976 Wait until operation completes. Completion is indicated by\r
977 setting of an appropriate variable.\r
978\r
979 @param[in] Context A pointer to the HTTP download context.\r
980 @param[in, out] CallBackComplete A pointer to the callback completion\r
981 variable set by the callback.\r
982\r
983 @retval EFI_SUCCESS Callback signalled completion.\r
984 @retval EFI_TIMEOUT Timed out waiting for completion.\r
985 @retval Others Error waiting for completion.\r
986**/\r
987STATIC\r
988EFI_STATUS\r
989WaitForCompletion (\r
990 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
991 IN OUT BOOLEAN *CallBackComplete\r
992 )\r
993{\r
47d20b54
MK
994 EFI_STATUS Status;\r
995 EFI_EVENT WaitEvt;\r
d8ab884f
VO
996\r
997 Status = EFI_SUCCESS;\r
998\r
999 //\r
1000 // Use a timer to measure timeout. Cannot use Stall here!\r
1001 //\r
1002 Status = gBS->CreateEvent (\r
1003 EVT_TIMER,\r
1004 TPL_CALLBACK,\r
1005 NULL,\r
1006 NULL,\r
1007 &WaitEvt\r
1008 );\r
47d20b54 1009 ASSERT_EFI_ERROR (Status);\r
d8ab884f 1010\r
47d20b54
MK
1011 if (!EFI_ERROR (Status)) {\r
1012 Status = gBS->SetTimer (\r
1013 WaitEvt,\r
1014 TimerRelative,\r
1015 EFI_TIMER_PERIOD_SECONDS (TIMER_MAX_TIMEOUT_S)\r
1016 );\r
d8ab884f 1017\r
47d20b54
MK
1018 ASSERT_EFI_ERROR (Status);\r
1019 }\r
d8ab884f 1020\r
47d20b54
MK
1021 while ( !*CallBackComplete\r
1022 && (!EFI_ERROR (Status))\r
1023 && EFI_ERROR (gBS->CheckEvent (WaitEvt)))\r
d8ab884f
VO
1024 {\r
1025 Status = Context->Http->Poll (Context->Http);\r
47d20b54
MK
1026 if ( !Context->ContentDownloaded\r
1027 && (CallBackComplete == &gResponseCallbackComplete))\r
d8ab884f
VO
1028 {\r
1029 //\r
1030 // An HTTP server may just send a response redirection header.\r
1031 // In this case, don't wait for the event as\r
1032 // it might never happen and we waste 10s waiting.\r
1033 // Note that at this point Response may not has been populated,\r
1034 // so it needs to be checked first.\r
1035 //\r
47d20b54
MK
1036 if ( Context->ResponseToken.Message\r
1037 && Context->ResponseToken.Message->Data.Response\r
1038 && (NEED_REDIRECTION (\r
1039 Context->ResponseToken.Message->Data.Response->StatusCode\r
1040 )\r
1041 ))\r
d8ab884f
VO
1042 {\r
1043 break;\r
1044 }\r
1045 }\r
1046 }\r
1047\r
1048 gBS->SetTimer (WaitEvt, TimerCancel, 0);\r
1049 gBS->CloseEvent (WaitEvt);\r
1050\r
1051 if (*CallBackComplete) {\r
1052 return EFI_SUCCESS;\r
1053 }\r
1054\r
1055 if (!EFI_ERROR (Status)) {\r
1056 Status = EFI_TIMEOUT;\r
1057 }\r
1058\r
1059 return Status;\r
1060}\r
1061\r
1062/**\r
1063 Generate and send a request to the http server.\r
1064\r
1065 @param[in] Context HTTP download context.\r
1066 @param[in] DownloadUrl Fully qualified URL to be downloaded.\r
1067\r
1068 @retval EFI_SUCCESS Request has been sent successfully.\r
1069 @retval EFI_INVALID_PARAMETER Invalid URL.\r
1070 @retval EFI_OUT_OF_RESOURCES Out of memory.\r
1071 @retval EFI_DEVICE_ERROR If HTTPS is used, this probably\r
1072 means that TLS support either was not\r
1073 installed or not configured.\r
1074 @retval Others Error sending the request.\r
1075**/\r
1076STATIC\r
1077EFI_STATUS\r
1078SendRequest (\r
1079 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1080 IN CHAR16 *DownloadUrl\r
1081 )\r
1082{\r
47d20b54
MK
1083 EFI_HTTP_REQUEST_DATA RequestData;\r
1084 EFI_HTTP_HEADER RequestHeader[HdrMax];\r
1085 EFI_HTTP_MESSAGE RequestMessage;\r
1086 EFI_STATUS Status;\r
1087 CHAR16 *Host;\r
1088 UINTN StringSize;\r
d8ab884f
VO
1089\r
1090 ZeroMem (&RequestData, sizeof (RequestData));\r
1091 ZeroMem (&RequestHeader, sizeof (RequestHeader));\r
1092 ZeroMem (&RequestMessage, sizeof (RequestMessage));\r
1093 ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));\r
1094\r
47d20b54
MK
1095 RequestHeader[HdrHost].FieldName = "Host";\r
1096 RequestHeader[HdrConn].FieldName = "Connection";\r
d8ab884f
VO
1097 RequestHeader[HdrAgent].FieldName = "User-Agent";\r
1098\r
1099 Host = (CHAR16 *)Context->ServerAddrAndProto;\r
1100 while (*Host != CHAR_NULL && *Host != L'/') {\r
1101 Host++;\r
1102 }\r
1103\r
1104 if (*Host == CHAR_NULL) {\r
1105 return EFI_INVALID_PARAMETER;\r
1106 }\r
1107\r
1108 //\r
1109 // Get the next slash.\r
1110 //\r
1111 Host++;\r
1112 //\r
1113 // And now the host name.\r
1114 //\r
1115 Host++;\r
1116\r
47d20b54 1117 StringSize = StrLen (Host) + 1;\r
d8ab884f
VO
1118 RequestHeader[HdrHost].FieldValue = AllocatePool (StringSize);\r
1119 if (!RequestHeader[HdrHost].FieldValue) {\r
1120 return EFI_OUT_OF_RESOURCES;\r
1121 }\r
1122\r
1123 UnicodeStrToAsciiStrS (\r
1124 Host,\r
1125 RequestHeader[HdrHost].FieldValue,\r
1126 StringSize\r
1127 );\r
1128\r
47d20b54 1129 RequestHeader[HdrConn].FieldValue = "close";\r
d8ab884f 1130 RequestHeader[HdrAgent].FieldValue = USER_AGENT_HDR;\r
47d20b54 1131 RequestMessage.HeaderCount = HdrMax;\r
d8ab884f
VO
1132\r
1133 RequestData.Method = HttpMethodGet;\r
47d20b54 1134 RequestData.Url = DownloadUrl;\r
d8ab884f
VO
1135\r
1136 RequestMessage.Data.Request = &RequestData;\r
47d20b54
MK
1137 RequestMessage.Headers = RequestHeader;\r
1138 RequestMessage.BodyLength = 0;\r
1139 RequestMessage.Body = NULL;\r
d8ab884f
VO
1140 Context->RequestToken.Event = NULL;\r
1141\r
1142 //\r
1143 // Completion callback event to be set when Request completes.\r
1144 //\r
1145 Status = gBS->CreateEvent (\r
1146 EVT_NOTIFY_SIGNAL,\r
1147 TPL_CALLBACK,\r
1148 RequestCallback,\r
1149 Context,\r
1150 &Context->RequestToken.Event\r
1151 );\r
1152 ASSERT_EFI_ERROR (Status);\r
1153\r
47d20b54 1154 Context->RequestToken.Status = EFI_SUCCESS;\r
d8ab884f 1155 Context->RequestToken.Message = &RequestMessage;\r
47d20b54
MK
1156 gRequestCallbackComplete = FALSE;\r
1157 Status = Context->Http->Request (Context->Http, &Context->RequestToken);\r
d8ab884f
VO
1158 if (EFI_ERROR (Status)) {\r
1159 goto Error;\r
1160 }\r
1161\r
1162 Status = WaitForCompletion (Context, &gRequestCallbackComplete);\r
1163 if (EFI_ERROR (Status)) {\r
1164 Context->Http->Cancel (Context->Http, &Context->RequestToken);\r
1165 }\r
1166\r
1167Error:\r
1168 SHELL_FREE_NON_NULL (RequestHeader[HdrHost].FieldValue);\r
1169 if (Context->RequestToken.Event) {\r
1170 gBS->CloseEvent (Context->RequestToken.Event);\r
1171 ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));\r
1172 }\r
1173\r
1174 return Status;\r
1175}\r
1176\r
1177/**\r
1178 Update the progress of a file download\r
1179 This procedure is called each time a new HTTP body portion is received.\r
1180\r
1181 @param[in] Context HTTP download context.\r
1182 @param[in] DownloadLen Portion size, in bytes.\r
1183 @param[in] Buffer The pointer to the parsed buffer.\r
1184\r
1185 @retval EFI_SUCCESS Portion saved.\r
1186 @retval Other Error saving the portion.\r
1187**/\r
1188STATIC\r
1189EFI_STATUS\r
1190EFIAPI\r
1191SavePortion (\r
1192 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1193 IN UINTN DownloadLen,\r
1194 IN CHAR8 *Buffer\r
1195 )\r
1196{\r
47d20b54
MK
1197 CHAR16 Progress[HTTP_PROGRESS_MESSAGE_SIZE];\r
1198 UINTN NbOfKb;\r
1199 UINTN Index;\r
1200 UINTN LastStep;\r
1201 UINTN Step;\r
1202 EFI_STATUS Status;\r
d8ab884f
VO
1203\r
1204 LastStep = 0;\r
47d20b54 1205 Step = 0;\r
d8ab884f
VO
1206\r
1207 ShellSetFilePosition (mFileHandle, Context->LastReportedNbOfBytes);\r
1208 Status = ShellWriteFile (mFileHandle, &DownloadLen, Buffer);\r
1209 if (EFI_ERROR (Status)) {\r
1210 if (Context->ContentDownloaded > 0) {\r
1211 PRINT_HII (STRING_TOKEN (STR_GEN_CRLF), NULL);\r
1212 }\r
1213\r
1214 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_WRITE), mLocalFilePath, Status);\r
1215 return Status;\r
1216 }\r
1217\r
1218 if (Context->ContentDownloaded == 0) {\r
1219 ShellPrintEx (-1, -1, L"%s 0 Kb", HTTP_PROGR_FRAME);\r
1220 }\r
1221\r
1222 Context->ContentDownloaded += DownloadLen;\r
47d20b54 1223 NbOfKb = Context->ContentDownloaded >> 10;\r
d8ab884f
VO
1224\r
1225 Progress[0] = L'\0';\r
1226 if (Context->ContentLength) {\r
47d20b54
MK
1227 LastStep = (Context->LastReportedNbOfBytes * HTTP_PROGRESS_SLIDER_STEPS) /\r
1228 Context->ContentLength;\r
1229 Step = (Context->ContentDownloaded * HTTP_PROGRESS_SLIDER_STEPS) /\r
1230 Context->ContentLength;\r
d8ab884f
VO
1231 }\r
1232\r
1233 Context->LastReportedNbOfBytes = Context->ContentDownloaded;\r
1234\r
1235 if (Step <= LastStep) {\r
1236 if (!Context->ContentLength) {\r
1237 //\r
1238 // Update downloaded size, there is no length info available.\r
1239 //\r
1240 ShellPrintEx (-1, -1, L"%s", HTTP_KB);\r
1241 ShellPrintEx (-1, -1, L"%7d Kb", NbOfKb);\r
1242 }\r
1243\r
1244 return EFI_SUCCESS;\r
1245 }\r
1246\r
1247 ShellPrintEx (-1, -1, L"%s", HTTP_PROGRESS_DEL);\r
1248\r
1249 Status = StrCpyS (Progress, HTTP_PROGRESS_MESSAGE_SIZE, HTTP_PROGR_FRAME);\r
1250 if (EFI_ERROR (Status)) {\r
1251 return Status;\r
1252 }\r
1253\r
1254 for (Index = 1; Index < Step; Index++) {\r
1255 Progress[Index] = L'=';\r
1256 }\r
1257\r
1258 if (Step) {\r
1259 Progress[Step] = L'>';\r
1260 }\r
1261\r
1262 UnicodeSPrint (\r
1263 Progress + (sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) - 1,\r
1264 sizeof (Progress) - sizeof (HTTP_PROGR_FRAME),\r
1265 L" %7d Kb",\r
1266 NbOfKb\r
1267 );\r
1268\r
d8ab884f
VO
1269 ShellPrintEx (-1, -1, L"%s", Progress);\r
1270\r
1271 return EFI_SUCCESS;\r
1272}\r
1273\r
1274/**\r
1275 Replace the original Host and Uri with Host and Uri returned by the\r
1276 HTTP server in 'Location' header (redirection).\r
1277\r
1278 @param[in] Location A pointer to the 'Location' string\r
1279 provided by HTTP server.\r
1280 @param[in] Context A pointer to HTTP download context.\r
1281 @param[in] DownloadUrl Fully qualified HTTP URL.\r
1282\r
1283 @retval EFI_SUCCESS Host and Uri were successfully set.\r
1284 @retval EFI_OUT_OF_RESOURCES Error setting Host or Uri.\r
1285**/\r
1286STATIC\r
1287EFI_STATUS\r
1288SetHostURI (\r
47d20b54
MK
1289 IN CHAR8 *Location,\r
1290 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1291 IN CHAR16 *DownloadUrl\r
d8ab884f
VO
1292 )\r
1293{\r
47d20b54
MK
1294 EFI_STATUS Status;\r
1295 UINTN StringSize;\r
1296 UINTN FirstStep;\r
1297 UINTN Idx;\r
1298 UINTN Step;\r
1299 CHAR8 *Walker;\r
1300 CHAR16 *Temp;\r
1301 CHAR8 *Tmp;\r
1302 CHAR16 *Url;\r
1303 BOOLEAN IsAbEmptyUrl;\r
1304\r
1305 Tmp = NULL;\r
1306 Url = NULL;\r
d8ab884f 1307 IsAbEmptyUrl = FALSE;\r
47d20b54 1308 FirstStep = 0;\r
d8ab884f
VO
1309\r
1310 StringSize = (AsciiStrSize (Location) * sizeof (CHAR16));\r
47d20b54 1311 Url = AllocateZeroPool (StringSize);\r
d8ab884f
VO
1312 if (!Url) {\r
1313 return EFI_OUT_OF_RESOURCES;\r
1314 }\r
1315\r
1316 Status = AsciiStrToUnicodeStrS (\r
1317 (CONST CHAR8 *)Location,\r
1318 Url,\r
1319 StringSize\r
1320 );\r
1321\r
1322 if (EFI_ERROR (Status)) {\r
47d20b54 1323 goto Error;\r
d8ab884f
VO
1324 }\r
1325\r
1326 //\r
1327 // If an HTTP server redirects to the same location more than once,\r
1328 // then stop attempts and tell it is not reachable.\r
1329 //\r
1330 if (!StrCmp (Url, DownloadUrl)) {\r
1331 Status = EFI_NO_MAPPING;\r
1332 goto Error;\r
1333 }\r
1334\r
1335 if (AsciiStrLen (Location) > 2) {\r
1336 //\r
1337 // Some servers return 'Location: //server/resource'\r
1338 //\r
1339 IsAbEmptyUrl = (Location[0] == '/') && (Location[1] == '/');\r
1340 if (IsAbEmptyUrl) {\r
1341 //\r
1342 // Skip first "//"\r
1343 //\r
1344 Location += 2;\r
1345 FirstStep = 1;\r
1346 }\r
1347 }\r
1348\r
1349 if (AsciiStrStr (Location, "://") || IsAbEmptyUrl) {\r
47d20b54 1350 Idx = 0;\r
d8ab884f
VO
1351 Walker = Location;\r
1352\r
1353 for (Step = FirstStep; Step < 2; Step++) {\r
47d20b54 1354 for ( ; *Walker != '/' && *Walker != '\0'; Walker++) {\r
d8ab884f
VO
1355 Idx++;\r
1356 }\r
1357\r
1358 if (!Step) {\r
1359 //\r
1360 // Skip "//"\r
1361 //\r
47d20b54 1362 Idx += 2;\r
d8ab884f
VO
1363 Walker += 2;\r
1364 }\r
1365 }\r
1366\r
1367 Tmp = AllocateZeroPool (Idx + 1);\r
1368 if (!Tmp) {\r
1369 Status = EFI_OUT_OF_RESOURCES;\r
1370 goto Error;\r
1371 }\r
1372\r
1373 CopyMem (Tmp, Location, Idx);\r
1374\r
1375 //\r
1376 // Location now points to Uri\r
1377 //\r
47d20b54 1378 Location += Idx;\r
d8ab884f
VO
1379 StringSize = (Idx + 1) * sizeof (CHAR16);\r
1380\r
1381 SHELL_FREE_NON_NULL (Context->ServerAddrAndProto);\r
1382\r
1383 Temp = AllocateZeroPool (StringSize);\r
1384 if (!Temp) {\r
1385 Status = EFI_OUT_OF_RESOURCES;\r
1386 goto Error;\r
1387 }\r
1388\r
1389 Status = AsciiStrToUnicodeStrS (\r
1390 (CONST CHAR8 *)Tmp,\r
1391 Temp,\r
1392 StringSize\r
1393 );\r
1394 if (EFI_ERROR (Status)) {\r
1395 SHELL_FREE_NON_NULL (Temp);\r
1396 goto Error;\r
1397 }\r
1398\r
1399 Idx = 0;\r
1400 if (IsAbEmptyUrl) {\r
1401 Context->ServerAddrAndProto = StrnCatGrow (\r
1402 &Context->ServerAddrAndProto,\r
1403 &Idx,\r
1404 L"http://",\r
1405 StrLen (L"http://")\r
1406 );\r
1407 }\r
1408\r
1409 Context->ServerAddrAndProto = StrnCatGrow (\r
1410 &Context->ServerAddrAndProto,\r
1411 &Idx,\r
1412 Temp,\r
1413 StrLen (Temp)\r
1414 );\r
1415 SHELL_FREE_NON_NULL (Temp);\r
1416 if (!Context->ServerAddrAndProto) {\r
1417 Status = EFI_OUT_OF_RESOURCES;\r
1418 goto Error;\r
1419 }\r
1420 }\r
1421\r
1422 SHELL_FREE_NON_NULL (Context->Uri);\r
1423\r
47d20b54 1424 StringSize = AsciiStrSize (Location) * sizeof (CHAR16);\r
d8ab884f
VO
1425 Context->Uri = AllocateZeroPool (StringSize);\r
1426 if (!Context->Uri) {\r
1427 Status = EFI_OUT_OF_RESOURCES;\r
1428 goto Error;\r
1429 }\r
1430\r
1431 //\r
1432 // Now make changes to the Uri part.\r
1433 //\r
1434 Status = AsciiStrToUnicodeStrS (\r
1435 (CONST CHAR8 *)Location,\r
1436 Context->Uri,\r
1437 StringSize\r
1438 );\r
1439Error:\r
1440 SHELL_FREE_NON_NULL (Tmp);\r
1441 SHELL_FREE_NON_NULL (Url);\r
1442\r
1443 return Status;\r
1444}\r
1445\r
1446/**\r
1447 Message parser callback.\r
1448 Save a portion of HTTP body.\r
1449\r
1450 @param[in] EventType Type of event. Can be either\r
1451 OnComplete or OnData.\r
1452 @param[in] Data A pointer to the buffer with data.\r
1453 @param[in] Length Data length of this portion.\r
1454 @param[in] Context A pointer to the HTTP download context.\r
1455\r
1456 @retval EFI_SUCCESS The portion was processed successfully.\r
1457 @retval Other Error returned by SavePortion.\r
1458**/\r
1459STATIC\r
1460EFI_STATUS\r
1461EFIAPI\r
1462ParseMsg (\r
47d20b54
MK
1463 IN HTTP_BODY_PARSE_EVENT EventType,\r
1464 IN CHAR8 *Data,\r
1465 IN UINTN Length,\r
1466 IN VOID *Context\r
d8ab884f
VO
1467 )\r
1468{\r
47d20b54
MK
1469 if ( (Data == NULL)\r
1470 || (EventType == BodyParseEventOnComplete)\r
1471 || (Context == NULL))\r
d8ab884f
VO
1472 {\r
1473 return EFI_SUCCESS;\r
1474 }\r
1475\r
1476 return SavePortion (Context, Length, Data);\r
1477}\r
1478\r
d8ab884f
VO
1479/**\r
1480 Get HTTP server response and collect the whole body as a file.\r
1481 Set appropriate status in Context (REQ_OK, REQ_REPEAT, REQ_ERROR).\r
1482 Note that even if HTTP server returns an error code, it might send\r
1483 the body as well. This body will be collected in the resultant file.\r
1484\r
1485 @param[in] Context A pointer to the HTTP download context.\r
1486 @param[in] DownloadUrl A pointer to the fully qualified URL to download.\r
1487\r
1488 @retval EFI_SUCCESS Valid file. Body successfully collected.\r
1489 @retval EFI_HTTP_ERROR Response is a valid HTTP response, but the\r
1490 HTTP server\r
1491 indicated an error (HTTP code >= 400).\r
1492 Response body MAY contain full\r
1493 HTTP server response.\r
1494 @retval Others Error getting the reponse from the HTTP server.\r
1495 Response body is not collected.\r
1496**/\r
1497STATIC\r
1498EFI_STATUS\r
1499GetResponse (\r
47d20b54
MK
1500 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1501 IN CHAR16 *DownloadUrl\r
d8ab884f
VO
1502 )\r
1503{\r
47d20b54
MK
1504 EFI_HTTP_RESPONSE_DATA ResponseData;\r
1505 EFI_HTTP_MESSAGE ResponseMessage;\r
1506 EFI_HTTP_HEADER *Header;\r
1507 EFI_STATUS Status;\r
1508 VOID *MsgParser;\r
1509 EFI_TIME StartTime;\r
1510 EFI_TIME EndTime;\r
1511 CONST CHAR16 *Desc;\r
1512 UINTN ElapsedSeconds;\r
1513 BOOLEAN IsTrunked;\r
1514 BOOLEAN CanMeasureTime;\r
d8ab884f
VO
1515\r
1516 ZeroMem (&ResponseData, sizeof (ResponseData));\r
1517 ZeroMem (&ResponseMessage, sizeof (ResponseMessage));\r
1518 ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));\r
1519 IsTrunked = FALSE;\r
1520\r
47d20b54
MK
1521 ResponseMessage.Body = Context->Buffer;\r
1522 Context->ResponseToken.Status = EFI_SUCCESS;\r
d8ab884f 1523 Context->ResponseToken.Message = &ResponseMessage;\r
47d20b54
MK
1524 Context->ContentLength = 0;\r
1525 Context->Status = REQ_OK;\r
1526 Status = EFI_SUCCESS;\r
1527 MsgParser = NULL;\r
1528 ResponseData.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;\r
1529 ResponseMessage.Data.Response = &ResponseData;\r
1530 Context->ResponseToken.Event = NULL;\r
1531 CanMeasureTime = FALSE;\r
d8ab884f
VO
1532 if (Context->Flags & DL_FLAG_TIME) {\r
1533 ZeroMem (&StartTime, sizeof (StartTime));\r
1534 CanMeasureTime = !EFI_ERROR (gRT->GetTime (&StartTime, NULL));\r
1535 }\r
1536\r
1537 do {\r
1538 SHELL_FREE_NON_NULL (ResponseMessage.Headers);\r
1539 ResponseMessage.HeaderCount = 0;\r
47d20b54
MK
1540 gResponseCallbackComplete = FALSE;\r
1541 ResponseMessage.BodyLength = Context->BufferSize;\r
d8ab884f
VO
1542\r
1543 if (ShellGetExecutionBreakFlag ()) {\r
1544 Status = EFI_ABORTED;\r
1545 break;\r
1546 }\r
1547\r
1548 if (!Context->ContentDownloaded && !Context->ResponseToken.Event) {\r
1549 Status = gBS->CreateEvent (\r
1550 EVT_NOTIFY_SIGNAL,\r
1551 TPL_CALLBACK,\r
1552 ResponseCallback,\r
1553 Context,\r
1554 &Context->ResponseToken.Event\r
1555 );\r
1556 ASSERT_EFI_ERROR (Status);\r
1557 } else {\r
1558 ResponseMessage.Data.Response = NULL;\r
1559 }\r
1560\r
1561 if (EFI_ERROR (Status)) {\r
1562 break;\r
1563 }\r
1564\r
1565 Status = Context->Http->Response (Context->Http, &Context->ResponseToken);\r
1566 if (EFI_ERROR (Status)) {\r
1567 break;\r
1568 }\r
1569\r
1570 Status = WaitForCompletion (Context, &gResponseCallbackComplete);\r
1571 if (EFI_ERROR (Status) && ResponseMessage.HeaderCount) {\r
1572 Status = EFI_SUCCESS;\r
1573 }\r
1574\r
1575 if (EFI_ERROR (Status)) {\r
1576 Context->Http->Cancel (Context->Http, &Context->ResponseToken);\r
1577 break;\r
1578 }\r
1579\r
1580 if (!Context->ContentDownloaded) {\r
1581 if (NEED_REDIRECTION (ResponseData.StatusCode)) {\r
1582 //\r
1583 // Need to repeat the request with new Location (server redirected).\r
1584 //\r
1585 Context->Status = REQ_NEED_REPEAT;\r
1586\r
1587 Header = HttpFindHeader (\r
1588 ResponseMessage.HeaderCount,\r
1589 ResponseMessage.Headers,\r
1590 "Location"\r
1591 );\r
1592 if (Header) {\r
1593 Status = SetHostURI (Header->FieldValue, Context, DownloadUrl);\r
1594 if (Status == EFI_NO_MAPPING) {\r
1595 PRINT_HII (\r
1596 STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),\r
1597 Context->ServerAddrAndProto,\r
1598 L"Recursive HTTP server relocation",\r
1599 Context->Uri\r
1600 );\r
1601 }\r
1602 } else {\r
1603 //\r
1604 // Bad reply from the server. Server must specify the location.\r
1605 // Indicate that resource was not found, and no body collected.\r
1606 //\r
1607 Status = EFI_NOT_FOUND;\r
1608 }\r
1609\r
1610 Context->Http->Cancel (Context->Http, &Context->ResponseToken);\r
1611 break;\r
1612 }\r
1613\r
1614 //\r
1615 // Init message-body parser by header information.\r
1616 //\r
1617 if (!MsgParser) {\r
1618 Status = HttpInitMsgParser (\r
1619 ResponseMessage.Data.Request->Method,\r
1620 ResponseData.StatusCode,\r
1621 ResponseMessage.HeaderCount,\r
1622 ResponseMessage.Headers,\r
1623 ParseMsg,\r
1624 Context,\r
1625 &MsgParser\r
1626 );\r
1627 if (EFI_ERROR (Status)) {\r
1628 break;\r
1629 }\r
1630 }\r
1631\r
1632 //\r
1633 // If it is a trunked message, rely on the parser.\r
1634 //\r
1635 Header = HttpFindHeader (\r
1636 ResponseMessage.HeaderCount,\r
1637 ResponseMessage.Headers,\r
1638 "Transfer-Encoding"\r
1639 );\r
1640 IsTrunked = (Header && !AsciiStrCmp (Header->FieldValue, "chunked"));\r
1641\r
1642 HttpGetEntityLength (MsgParser, &Context->ContentLength);\r
1643\r
47d20b54
MK
1644 if ( (ResponseData.StatusCode >= HTTP_STATUS_400_BAD_REQUEST)\r
1645 && (ResponseData.StatusCode != HTTP_STATUS_308_PERMANENT_REDIRECT))\r
d8ab884f
VO
1646 {\r
1647 //\r
1648 // Server reported an error via Response code.\r
1649 // Collect the body if any.\r
1650 //\r
1651 if (!gHttpError) {\r
1652 gHttpError = TRUE;\r
1653\r
1654 Desc = ErrStatusDesc[ResponseData.StatusCode -\r
1655 HTTP_STATUS_400_BAD_REQUEST];\r
1656 PRINT_HII (\r
1657 STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),\r
1658 Context->ServerAddrAndProto,\r
1659 Desc,\r
1660 Context->Uri\r
1661 );\r
1662\r
1663 //\r
1664 // This gives an RFC HTTP error.\r
1665 //\r
1666 Context->Status = ShellStrToUintn (Desc);\r
47d20b54 1667 Status = ENCODE_ERROR (Context->Status);\r
d8ab884f
VO
1668 }\r
1669 }\r
1670 }\r
1671\r
1672 //\r
1673 // Do NOT try to parse an empty body.\r
1674 //\r
1675 if (ResponseMessage.BodyLength || IsTrunked) {\r
1676 Status = HttpParseMessageBody (\r
1677 MsgParser,\r
1678 ResponseMessage.BodyLength,\r
1679 ResponseMessage.Body\r
1680 );\r
1681 }\r
47d20b54
MK
1682 } while ( !HttpIsMessageComplete (MsgParser)\r
1683 && !EFI_ERROR (Status)\r
1684 && ResponseMessage.BodyLength);\r
d8ab884f 1685\r
47d20b54
MK
1686 if ( (Context->Status != REQ_NEED_REPEAT)\r
1687 && (Status == EFI_SUCCESS)\r
1688 && CanMeasureTime)\r
d8ab884f
VO
1689 {\r
1690 if (!EFI_ERROR (gRT->GetTime (&EndTime, NULL))) {\r
1691 ElapsedSeconds = EfiTimeToEpoch (&EndTime) - EfiTimeToEpoch (&StartTime);\r
1692 Print (\r
1693 L",%a%Lus\n",\r
1694 ElapsedSeconds ? " " : " < ",\r
1695 ElapsedSeconds > 1 ? (UINT64)ElapsedSeconds : 1\r
1696 );\r
1697 }\r
1698 }\r
1699\r
1700 SHELL_FREE_NON_NULL (MsgParser);\r
1701 if (Context->ResponseToken.Event) {\r
1702 gBS->CloseEvent (Context->ResponseToken.Event);\r
1703 ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));\r
1704 }\r
1705\r
1706 return Status;\r
1707}\r
1708\r
1709/**\r
1710 Worker function that downloads the data of a file from an HTTP server given\r
1711 the path of the file and its size.\r
1712\r
1713 @param[in] Context A pointer to the HTTP download context.\r
1714 @param[in] ControllerHandle The handle of the network interface controller\r
1715 @param[in] NicName NIC name\r
1716\r
1717 @retval EFI_SUCCESS The file was downloaded.\r
1718 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
1719 #return EFI_HTTP_ERROR The server returned a valid HTTP error.\r
1720 Examine the mLocalFilePath file\r
1721 to get error body.\r
1722 @retval Others The downloading of the file from the server\r
1723 failed.\r
1724**/\r
1725STATIC\r
1726EFI_STATUS\r
1727DownloadFile (\r
47d20b54
MK
1728 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1729 IN EFI_HANDLE ControllerHandle,\r
1730 IN CHAR16 *NicName\r
d8ab884f
VO
1731 )\r
1732{\r
47d20b54
MK
1733 EFI_STATUS Status;\r
1734 CHAR16 *DownloadUrl;\r
1735 UINTN UrlSize;\r
1736 EFI_HANDLE HttpChildHandle;\r
d8ab884f
VO
1737\r
1738 ASSERT (Context);\r
1739 if (Context == NULL) {\r
1740 return EFI_INVALID_PARAMETER;\r
1741 }\r
1742\r
47d20b54 1743 DownloadUrl = NULL;\r
d8ab884f
VO
1744 HttpChildHandle = NULL;\r
1745\r
1746 Context->Buffer = AllocatePool (Context->BufferSize);\r
1747 if (Context->Buffer == NULL) {\r
1748 Status = EFI_OUT_OF_RESOURCES;\r
1749 goto ON_EXIT;\r
1750 }\r
1751\r
1752 //\r
1753 // Open the file.\r
1754 //\r
1755 if (!EFI_ERROR (ShellFileExists (mLocalFilePath))) {\r
1756 ShellDeleteFileByName (mLocalFilePath);\r
1757 }\r
1758\r
1759 Status = ShellOpenFileByName (\r
1760 mLocalFilePath,\r
1761 &mFileHandle,\r
1762 EFI_FILE_MODE_CREATE |\r
1763 EFI_FILE_MODE_WRITE |\r
1764 EFI_FILE_MODE_READ,\r
1765 0\r
1766 );\r
1767 if (EFI_ERROR (Status)) {\r
1768 PRINT_HII_APP (STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), mLocalFilePath);\r
1769 goto ON_EXIT;\r
1770 }\r
1771\r
1772 do {\r
1773 SHELL_FREE_NON_NULL (DownloadUrl);\r
1774\r
1775 CLOSE_HTTP_HANDLE (ControllerHandle, HttpChildHandle);\r
1776\r
1777 Status = CreateServiceChildAndOpenProtocol (\r
1778 ControllerHandle,\r
1779 &gEfiHttpServiceBindingProtocolGuid,\r
1780 &gEfiHttpProtocolGuid,\r
1781 &HttpChildHandle,\r
47d20b54 1782 (VOID **)&Context->Http\r
d8ab884f
VO
1783 );\r
1784\r
1785 if (EFI_ERROR (Status)) {\r
1786 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_OPEN_PROTOCOL), NicName, Status);\r
1787 goto ON_EXIT;\r
1788 }\r
1789\r
1790 Status = Context->Http->Configure (Context->Http, &Context->HttpConfigData);\r
1791 if (EFI_ERROR (Status)) {\r
1792 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_CONFIGURE), NicName, Status);\r
1793 goto ON_EXIT;\r
1794 }\r
1795\r
47d20b54 1796 UrlSize = 0;\r
d8ab884f
VO
1797 DownloadUrl = StrnCatGrow (\r
1798 &DownloadUrl,\r
1799 &UrlSize,\r
1800 Context->ServerAddrAndProto,\r
1801 StrLen (Context->ServerAddrAndProto)\r
1802 );\r
1803 if (Context->Uri[0] != L'/') {\r
1804 DownloadUrl = StrnCatGrow (\r
1805 &DownloadUrl,\r
1806 &UrlSize,\r
1807 L"/",\r
1808 StrLen (Context->ServerAddrAndProto)\r
1809 );\r
1810 }\r
1811\r
1812 DownloadUrl = StrnCatGrow (\r
1813 &DownloadUrl,\r
1814 &UrlSize,\r
1815 Context->Uri,\r
47d20b54
MK
1816 StrLen (Context->Uri)\r
1817 );\r
d8ab884f
VO
1818\r
1819 PRINT_HII (STRING_TOKEN (STR_HTTP_DOWNLOADING), DownloadUrl);\r
1820\r
1821 Status = SendRequest (Context, DownloadUrl);\r
1822 if (Status) {\r
1823 goto ON_EXIT;\r
1824 }\r
1825\r
1826 Status = GetResponse (Context, DownloadUrl);\r
1827\r
1828 if (Status) {\r
1829 goto ON_EXIT;\r
1830 }\r
d8ab884f
VO
1831 } while (Context->Status == REQ_NEED_REPEAT);\r
1832\r
1833 if (Context->Status) {\r
1834 Status = ENCODE_ERROR (Context->Status);\r
1835 }\r
1836\r
1837ON_EXIT:\r
1838 //\r
1839 // Close the file.\r
1840 //\r
1841 if (mFileHandle != NULL) {\r
1842 if (EFI_ERROR (Status) && !(Context->Flags & DL_FLAG_KEEP_BAD)) {\r
1843 ShellDeleteFile (&mFileHandle);\r
1844 } else {\r
1845 ShellCloseFile (&mFileHandle);\r
1846 }\r
1847 }\r
1848\r
1849 SHELL_FREE_NON_NULL (DownloadUrl);\r
1850 SHELL_FREE_NON_NULL (Context->Buffer);\r
1851\r
1852 CLOSE_HTTP_HANDLE (ControllerHandle, HttpChildHandle);\r
1853\r
1854 return Status;\r
1855}\r
1856\r
1857/**\r
1858 Retrive HII package list from ImageHandle and publish to HII database.\r
1859\r
1860 @param[in] ImageHandle The image handle of the process.\r
1861\r
1862 @retval HII handle.\r
1863**/\r
1864EFI_HII_HANDLE\r
1865InitializeHiiPackage (\r
47d20b54 1866 IN EFI_HANDLE ImageHandle\r
d8ab884f
VO
1867 )\r
1868{\r
47d20b54
MK
1869 EFI_STATUS Status;\r
1870 EFI_HII_PACKAGE_LIST_HEADER *PackageList;\r
1871 EFI_HII_HANDLE HiiHandle;\r
d8ab884f
VO
1872\r
1873 //\r
1874 // Retrieve HII package list from ImageHandle.\r
1875 //\r
1876 Status = gBS->OpenProtocol (\r
1877 ImageHandle,\r
1878 &gEfiHiiPackageListProtocolGuid,\r
1879 (VOID **)&PackageList,\r
1880 ImageHandle,\r
1881 NULL,\r
1882 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1883 );\r
1884 ASSERT_EFI_ERROR (Status);\r
1885 if (EFI_ERROR (Status)) {\r
1886 return NULL;\r
1887 }\r
1888\r
1889 //\r
1890 // Publish HII package list to HII Database.\r
1891 //\r
1892 Status = gHiiDatabase->NewPackageList (\r
47d20b54
MK
1893 gHiiDatabase,\r
1894 PackageList,\r
1895 NULL,\r
1896 &HiiHandle\r
1897 );\r
d8ab884f
VO
1898 ASSERT_EFI_ERROR (Status);\r
1899 if (EFI_ERROR (Status)) {\r
1900 return NULL;\r
1901 }\r
1902\r
1903 return HiiHandle;\r
1904}\r