]> git.proxmox.com Git - mirror_edk2.git/blame - ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c
ShellPkg/HttpDynamicCommand: Fix possible uninitialized use
[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
14#define IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH 32\r
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
40#define DEFAULT_BUF_SIZE SIZE_32KB\r
41#define MAX_BUF_SIZE SIZE_4MB\r
42\r
43#define MIN_PARAM_COUNT 2\r
44#define MAX_PARAM_COUNT 4\r
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
50#define CLOSE_HTTP_HANDLE(ControllerHandle,HttpChildHandle) \\r
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
72#define TIMER_MAX_TIMEOUT_S 10\r
73\r
74//\r
75// File name to use when Uri ends with "/".\r
76//\r
77#define DEFAULT_HTML_FILE L"index.html"\r
78#define DEFAULT_HTTP_PROTO L"http"\r
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
88#define HTTP_KB L"\b\b\b\b\b\b\b\b\b\b"\r
89//\r
90// Frame for the progression slider.\r
91//\r
92#define HTTP_PROGR_FRAME L"[ ]"\r
93\r
94//\r
95// Improve readability by using these macros.\r
96//\r
97#define PRINT_HII(token,...) \\r
98 ShellPrintHiiEx (\\r
99 -1, -1, NULL, token, mHttpHiiHandle, __VA_ARGS__)\r
100\r
101#define PRINT_HII_APP(token,value) \\r
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
112#define EPOCH_JULIAN_DATE 2440588\r
113\r
114//\r
115// Seconds per unit.\r
116//\r
117#define SEC_PER_MIN ((UINTN) 60)\r
118#define SEC_PER_HOUR ((UINTN) 3600)\r
119#define SEC_PER_DAY ((UINTN) 86400)\r
120\r
121//\r
122// String descriptions for server errors.\r
123//\r
124STATIC CONST CHAR16 *ErrStatusDesc[] =\r
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
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
160};\r
161\r
162//\r
163// Local File Handle.\r
164//\r
165STATIC SHELL_FILE_HANDLE mFileHandle = NULL;\r
166\r
167//\r
168// Path of the local file, Unicode encoded.\r
169//\r
170STATIC CONST CHAR16 *mLocalFilePath;\r
171\r
172STATIC BOOLEAN gRequestCallbackComplete = FALSE;\r
173STATIC BOOLEAN gResponseCallbackComplete = FALSE;\r
174\r
175STATIC BOOLEAN gHttpError;\r
176\r
177EFI_HII_HANDLE mHttpHiiHandle;\r
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
289 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
290 IN EFI_HANDLE ControllerHandle,\r
291 IN CHAR16 *NicName\r
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
305 IN CHAR16 *String\r
306 )\r
307{\r
308 CHAR16 *Str;\r
309 UINTN Len;\r
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
331 if (!Len || (Str[Len - 1] != L' ' && Str[Len - 1] != '\t')) {\r
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
358 IN EFI_EVENT Event,\r
359 IN VOID *Context\r
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
374 IN EFI_EVENT Event,\r
375 IN VOID *Context\r
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
399 UINTN a;\r
400 UINTN y;\r
401 UINTN m;\r
402 //\r
403 // Absolute Julian Date representation of the supplied Time.\r
404 //\r
405 UINTN JulianDate;\r
406 //\r
407 // Number of days elapsed since EPOCH_JULIAN_DAY.\r
408 //\r
409 UINTN EpochDays;\r
410\r
411 a = (14 - Time->Month) / 12 ;\r
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
440 UINTN EpochDays;\r
441 UINTN EpochSeconds;\r
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
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
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
525 if ((Status == EFI_VOLUME_CORRUPTED)\r
526 && (ProblemParam != NULL))\r
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
557 Context.HttpConfigData.HttpVersion = HttpVersion11;\r
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
595 ParamOffset = 1;\r
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
632 (*Walker == L'/' ) ) {\r
633 break;\r
634 }\r
635 }\r
636\r
637 mLocalFilePath = Walker + 1;\r
638 }\r
639\r
640 if (!StrLen (mLocalFilePath)) {\r
641 mLocalFilePath = DEFAULT_HTML_FILE;\r
642 }\r
643\r
644 InitialSize = 0;\r
645 Context.Uri = StrnCatGrow (\r
646 &Context.Uri,\r
647 &InitialSize,\r
648 RemoteFilePath,\r
649 StrLen (RemoteFilePath)\r
650 );\r
651 if (!Context.Uri) {\r
652 Status = EFI_OUT_OF_RESOURCES;\r
653 goto Error;\r
654 }\r
655\r
656 //\r
657 // Get the name of the Network Interface Card to be used if any.\r
658 //\r
659 UserNicName = ShellCommandLineGetValue (CheckPackage, L"-i");\r
660\r
661 ValueStr = ShellCommandLineGetValue (CheckPackage, L"-l");\r
662 if ((ValueStr != NULL)\r
663 && (!StringToUint16 (\r
664 ValueStr,\r
665 &Context.HttpConfigData.AccessPoint.IPv4Node->LocalPort\r
666 )\r
667 ))\r
668 {\r
669 goto Error;\r
670 }\r
671\r
672 Context.BufferSize = DEFAULT_BUF_SIZE;\r
673\r
674 ValueStr = ShellCommandLineGetValue (CheckPackage, L"-s");\r
675 if (ValueStr != NULL) {\r
676 Context.BufferSize = ShellStrToUintn (ValueStr);\r
677 if (!Context.BufferSize || Context.BufferSize > MAX_BUF_SIZE) {\r
678 PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
679 goto Error;\r
680 }\r
681 }\r
682\r
683 ValueStr = ShellCommandLineGetValue (CheckPackage, L"-t");\r
684 if (ValueStr != NULL) {\r
685 Context.HttpConfigData.TimeOutMillisec = (UINT32)ShellStrToUintn (ValueStr);\r
686 }\r
687\r
688 //\r
689 // Locate all HTTP Service Binding protocols.\r
690 //\r
691 Status = gBS->LocateHandleBuffer (\r
692 ByProtocol,\r
693 &gEfiManagedNetworkServiceBindingProtocolGuid,\r
694 NULL,\r
695 &HandleCount,\r
696 &Handles\r
697 );\r
698 if (EFI_ERROR (Status) || (HandleCount == 0)) {\r
699 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NO_NIC), NULL);\r
700 if (!EFI_ERROR (Status)) {\r
701 Status = EFI_NOT_FOUND;\r
702 }\r
703\r
704 goto Error;\r
705 }\r
706\r
707 Status = EFI_NOT_FOUND;\r
708\r
709 Context.Flags = 0;\r
710 if (ShellCommandLineGetFlag (CheckPackage, L"-m")) {\r
711 Context.Flags |= DL_FLAG_TIME;\r
712 }\r
713\r
714 if (ShellCommandLineGetFlag (CheckPackage, L"-k")) {\r
715 Context.Flags |= DL_FLAG_KEEP_BAD;\r
716 }\r
717\r
718 for (NicNumber = 0;\r
719 (NicNumber < HandleCount) && (Status != EFI_SUCCESS);\r
720 NicNumber++)\r
721 {\r
722 ControllerHandle = Handles[NicNumber];\r
723\r
724 Status = GetNicName (ControllerHandle, NicNumber, NicName);\r
725 if (EFI_ERROR (Status)) {\r
726 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NIC_NAME), NicNumber, Status);\r
727 continue;\r
728 }\r
729\r
730 if (UserNicName != NULL) {\r
731 if (StrCmp (NicName, UserNicName) != 0) {\r
732 Status = EFI_NOT_FOUND;\r
733 continue;\r
734 }\r
735\r
736 NicFound = TRUE;\r
737 }\r
738\r
739 Status = DownloadFile (&Context, ControllerHandle, NicName);\r
740 PRINT_HII (STRING_TOKEN (STR_GEN_CRLF), NULL);\r
741\r
742 if (EFI_ERROR (Status)) {\r
743 PRINT_HII (\r
744 STRING_TOKEN (STR_HTTP_ERR_DOWNLOAD),\r
745 RemoteFilePath,\r
746 NicName,\r
747 Status\r
748 );\r
749 //\r
750 // If a user aborted the operation,\r
751 // do not try another controller.\r
752 //\r
753 if (Status == EFI_ABORTED) {\r
754 goto Error;\r
755 }\r
756 }\r
757\r
758 if (gHttpError) {\r
759 //\r
760 // This is not related to connection, so no need to repeat with\r
761 // another interface.\r
762 //\r
763 break;\r
764 }\r
765 }\r
766\r
767 if ((UserNicName != NULL) && (!NicFound)) {\r
768 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_NIC_NOT_FOUND), UserNicName);\r
769 }\r
770\r
771Error:\r
772 ShellCommandLineFreeVarList (CheckPackage);\r
773 SHELL_FREE_NON_NULL (Handles);\r
774 SHELL_FREE_NON_NULL (Context.ServerAddrAndProto);\r
775 SHELL_FREE_NON_NULL (Context.Uri);\r
776\r
777 return Status & ~MAX_BIT;\r
778}\r
779\r
780/**\r
781 Check and convert the UINT16 option values of the 'http' command\r
782\r
783 @param[in] ValueStr Value as an Unicode encoded string\r
784 @param[out] Value UINT16 value\r
785\r
786 @retval TRUE The value was returned.\r
787 @retval FALSE A parsing error occured.\r
788**/\r
789STATIC\r
790BOOLEAN\r
791StringToUint16 (\r
792 IN CONST CHAR16 *ValueStr,\r
793 OUT UINT16 *Value\r
794 )\r
795{\r
796 UINTN Val;\r
797\r
798 Val = ShellStrToUintn (ValueStr);\r
799 if (Val > MAX_UINT16) {\r
800 PRINT_HII_APP (STRING_TOKEN (STR_GEN_PARAM_INV), ValueStr);\r
801 return FALSE;\r
802 }\r
803\r
804 *Value = (UINT16)Val;\r
805 return TRUE;\r
806}\r
807\r
808/**\r
809 Get the name of the NIC.\r
810\r
811 @param[in] ControllerHandle The network physical device handle.\r
812 @param[in] NicNumber The network physical device number.\r
813 @param[out] NicName Address where to store the NIC name.\r
814 The memory area has to be at least\r
815 IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH\r
816 double byte wide.\r
817\r
818 @retval EFI_SUCCESS The name of the NIC was returned.\r
819 @retval Others The creation of the child for the Managed\r
820 Network Service failed or the opening of\r
821 the Managed Network Protocol failed or\r
822 the operational parameters for the\r
823 Managed Network Protocol could not be\r
824 read.\r
825**/\r
826STATIC\r
827EFI_STATUS\r
828GetNicName (\r
829 IN EFI_HANDLE ControllerHandle,\r
830 IN UINTN NicNumber,\r
831 OUT CHAR16 *NicName\r
832 )\r
833{\r
834 EFI_STATUS Status;\r
835 EFI_HANDLE MnpHandle;\r
836 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
837 EFI_SIMPLE_NETWORK_MODE SnpMode;\r
838\r
839 Status = CreateServiceChildAndOpenProtocol (\r
840 ControllerHandle,\r
841 &gEfiManagedNetworkServiceBindingProtocolGuid,\r
842 &gEfiManagedNetworkProtocolGuid,\r
843 &MnpHandle,\r
844 (VOID**)&Mnp\r
845 );\r
846 if (EFI_ERROR (Status)) {\r
847 goto Error;\r
848 }\r
849\r
850 Status = Mnp->GetModeData (Mnp, NULL, &SnpMode);\r
851 if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {\r
852 goto Error;\r
853 }\r
854\r
855 UnicodeSPrint (\r
856 NicName,\r
857 IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH,\r
858 SnpMode.IfType == NET_IFTYPE_ETHERNET ? L"eth%d" : L"unk%d",\r
859 NicNumber\r
860 );\r
861\r
862 Status = EFI_SUCCESS;\r
863\r
864Error:\r
865\r
866 if (MnpHandle != NULL) {\r
867 CloseProtocolAndDestroyServiceChild (\r
868 ControllerHandle,\r
869 &gEfiManagedNetworkServiceBindingProtocolGuid,\r
870 &gEfiManagedNetworkProtocolGuid,\r
871 MnpHandle\r
872 );\r
873 }\r
874\r
875 return Status;\r
876}\r
877\r
878/**\r
879 Create a child for the service identified by its service binding protocol GUID\r
880 and get from the child the interface of the protocol identified by its GUID.\r
881\r
882 @param[in] ControllerHandle Controller handle.\r
883 @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
884 service to be created.\r
885 @param[in] ProtocolGuid GUID of the protocol to be open.\r
886 @param[out] ChildHandle Address where the handler of the\r
887 created child is returned. NULL is\r
888 returned in case of error.\r
889 @param[out] Interface Address where a pointer to the\r
890 protocol interface is returned in\r
891 case of success.\r
892\r
893 @retval EFI_SUCCESS The child was created and the protocol opened.\r
894 @retval Others Either the creation of the child or the opening\r
895 of the protocol failed.\r
896**/\r
897STATIC\r
898EFI_STATUS\r
899CreateServiceChildAndOpenProtocol (\r
900 IN EFI_HANDLE ControllerHandle,\r
901 IN EFI_GUID *ServiceBindingProtocolGuid,\r
902 IN EFI_GUID *ProtocolGuid,\r
903 OUT EFI_HANDLE *ChildHandle,\r
904 OUT VOID **Interface\r
905 )\r
906{\r
907 EFI_STATUS Status;\r
908\r
909 *ChildHandle = NULL;\r
910 Status = NetLibCreateServiceChild (\r
911 ControllerHandle,\r
912 gImageHandle,\r
913 ServiceBindingProtocolGuid,\r
914 ChildHandle\r
915 );\r
916 if (!EFI_ERROR (Status)) {\r
917 Status = gBS->OpenProtocol (\r
918 *ChildHandle,\r
919 ProtocolGuid,\r
920 Interface,\r
921 gImageHandle,\r
922 ControllerHandle,\r
923 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
924 );\r
925 if (EFI_ERROR (Status)) {\r
926 NetLibDestroyServiceChild (\r
927 ControllerHandle,\r
928 gImageHandle,\r
929 ServiceBindingProtocolGuid,\r
930 *ChildHandle\r
931 );\r
932 *ChildHandle = NULL;\r
933 }\r
934 }\r
935\r
936 return Status;\r
937}\r
938\r
939/**\r
940 Close the protocol identified by its GUID on the child handle of the service\r
941 identified by its service binding protocol GUID, then destroy the child\r
942 handle.\r
943\r
944 @param[in] ControllerHandle Controller handle.\r
945 @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of the\r
946 service to be destroyed.\r
947 @param[in] ProtocolGuid GUID of the protocol to be closed.\r
948 @param[in] ChildHandle Handle of the child to be destroyed.\r
949**/\r
950STATIC\r
951VOID\r
952CloseProtocolAndDestroyServiceChild (\r
953 IN EFI_HANDLE ControllerHandle,\r
954 IN EFI_GUID *ServiceBindingProtocolGuid,\r
955 IN EFI_GUID *ProtocolGuid,\r
956 IN EFI_HANDLE ChildHandle\r
957 )\r
958{\r
959 gBS->CloseProtocol (\r
960 ChildHandle,\r
961 ProtocolGuid,\r
962 gImageHandle,\r
963 ControllerHandle\r
964 );\r
965\r
966 NetLibDestroyServiceChild (\r
967 ControllerHandle,\r
968 gImageHandle,\r
969 ServiceBindingProtocolGuid,\r
970 ChildHandle\r
971 );\r
972}\r
973\r
974/**\r
975 Wait until operation completes. Completion is indicated by\r
976 setting of an appropriate variable.\r
977\r
978 @param[in] Context A pointer to the HTTP download context.\r
979 @param[in, out] CallBackComplete A pointer to the callback completion\r
980 variable set by the callback.\r
981\r
982 @retval EFI_SUCCESS Callback signalled completion.\r
983 @retval EFI_TIMEOUT Timed out waiting for completion.\r
984 @retval Others Error waiting for completion.\r
985**/\r
986STATIC\r
987EFI_STATUS\r
988WaitForCompletion (\r
989 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
990 IN OUT BOOLEAN *CallBackComplete\r
991 )\r
992{\r
993 EFI_STATUS Status;\r
994 EFI_EVENT WaitEvt;\r
995\r
996 Status = EFI_SUCCESS;\r
997\r
998 //\r
999 // Use a timer to measure timeout. Cannot use Stall here!\r
1000 //\r
1001 Status = gBS->CreateEvent (\r
1002 EVT_TIMER,\r
1003 TPL_CALLBACK,\r
1004 NULL,\r
1005 NULL,\r
1006 &WaitEvt\r
1007 );\r
1008 ASSERT_EFI_ERROR (Status);\r
1009\r
1010 if (!EFI_ERROR (Status)) {\r
1011 Status = gBS->SetTimer (\r
1012 WaitEvt,\r
1013 TimerRelative,\r
1014 EFI_TIMER_PERIOD_SECONDS (TIMER_MAX_TIMEOUT_S)\r
1015 );\r
1016\r
1017 ASSERT_EFI_ERROR (Status);\r
1018 }\r
1019\r
1020 while (! *CallBackComplete\r
1021 && (!EFI_ERROR (Status))\r
1022 && EFI_ERROR (gBS->CheckEvent (WaitEvt)))\r
1023 {\r
1024 Status = Context->Http->Poll (Context->Http);\r
1025 if (!Context->ContentDownloaded\r
1026 && CallBackComplete == &gResponseCallbackComplete)\r
1027 {\r
1028 //\r
1029 // An HTTP server may just send a response redirection header.\r
1030 // In this case, don't wait for the event as\r
1031 // it might never happen and we waste 10s waiting.\r
1032 // Note that at this point Response may not has been populated,\r
1033 // so it needs to be checked first.\r
1034 //\r
1035 if (Context->ResponseToken.Message\r
1036 && Context->ResponseToken.Message->Data.Response\r
1037 && (NEED_REDIRECTION (\r
1038 Context->ResponseToken.Message->Data.Response->StatusCode\r
1039 )\r
1040 ))\r
1041 {\r
1042 break;\r
1043 }\r
1044 }\r
1045 }\r
1046\r
1047 gBS->SetTimer (WaitEvt, TimerCancel, 0);\r
1048 gBS->CloseEvent (WaitEvt);\r
1049\r
1050 if (*CallBackComplete) {\r
1051 return EFI_SUCCESS;\r
1052 }\r
1053\r
1054 if (!EFI_ERROR (Status)) {\r
1055 Status = EFI_TIMEOUT;\r
1056 }\r
1057\r
1058 return Status;\r
1059}\r
1060\r
1061/**\r
1062 Generate and send a request to the http server.\r
1063\r
1064 @param[in] Context HTTP download context.\r
1065 @param[in] DownloadUrl Fully qualified URL to be downloaded.\r
1066\r
1067 @retval EFI_SUCCESS Request has been sent successfully.\r
1068 @retval EFI_INVALID_PARAMETER Invalid URL.\r
1069 @retval EFI_OUT_OF_RESOURCES Out of memory.\r
1070 @retval EFI_DEVICE_ERROR If HTTPS is used, this probably\r
1071 means that TLS support either was not\r
1072 installed or not configured.\r
1073 @retval Others Error sending the request.\r
1074**/\r
1075STATIC\r
1076EFI_STATUS\r
1077SendRequest (\r
1078 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1079 IN CHAR16 *DownloadUrl\r
1080 )\r
1081{\r
1082 EFI_HTTP_REQUEST_DATA RequestData;\r
1083 EFI_HTTP_HEADER RequestHeader[HdrMax];\r
1084 EFI_HTTP_MESSAGE RequestMessage;\r
1085 EFI_STATUS Status;\r
1086 CHAR16 *Host;\r
1087 UINTN StringSize;\r
1088\r
1089 ZeroMem (&RequestData, sizeof (RequestData));\r
1090 ZeroMem (&RequestHeader, sizeof (RequestHeader));\r
1091 ZeroMem (&RequestMessage, sizeof (RequestMessage));\r
1092 ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));\r
1093\r
1094 RequestHeader[HdrHost].FieldName = "Host";\r
1095 RequestHeader[HdrConn].FieldName = "Connection";\r
1096 RequestHeader[HdrAgent].FieldName = "User-Agent";\r
1097\r
1098 Host = (CHAR16 *)Context->ServerAddrAndProto;\r
1099 while (*Host != CHAR_NULL && *Host != L'/') {\r
1100 Host++;\r
1101 }\r
1102\r
1103 if (*Host == CHAR_NULL) {\r
1104 return EFI_INVALID_PARAMETER;\r
1105 }\r
1106\r
1107 //\r
1108 // Get the next slash.\r
1109 //\r
1110 Host++;\r
1111 //\r
1112 // And now the host name.\r
1113 //\r
1114 Host++;\r
1115\r
1116 StringSize = StrLen (Host) + 1;\r
1117 RequestHeader[HdrHost].FieldValue = AllocatePool (StringSize);\r
1118 if (!RequestHeader[HdrHost].FieldValue) {\r
1119 return EFI_OUT_OF_RESOURCES;\r
1120 }\r
1121\r
1122 UnicodeStrToAsciiStrS (\r
1123 Host,\r
1124 RequestHeader[HdrHost].FieldValue,\r
1125 StringSize\r
1126 );\r
1127\r
1128 RequestHeader[HdrConn].FieldValue = "close";\r
1129 RequestHeader[HdrAgent].FieldValue = USER_AGENT_HDR;\r
1130 RequestMessage.HeaderCount = HdrMax;\r
1131\r
1132 RequestData.Method = HttpMethodGet;\r
1133 RequestData.Url = DownloadUrl;\r
1134\r
1135 RequestMessage.Data.Request = &RequestData;\r
1136 RequestMessage.Headers = RequestHeader;\r
1137 RequestMessage.BodyLength = 0;\r
1138 RequestMessage.Body = NULL;\r
1139 Context->RequestToken.Event = NULL;\r
1140\r
1141 //\r
1142 // Completion callback event to be set when Request completes.\r
1143 //\r
1144 Status = gBS->CreateEvent (\r
1145 EVT_NOTIFY_SIGNAL,\r
1146 TPL_CALLBACK,\r
1147 RequestCallback,\r
1148 Context,\r
1149 &Context->RequestToken.Event\r
1150 );\r
1151 ASSERT_EFI_ERROR (Status);\r
1152\r
1153 Context->RequestToken.Status = EFI_SUCCESS;\r
1154 Context->RequestToken.Message = &RequestMessage;\r
1155 gRequestCallbackComplete = FALSE;\r
1156 Status = Context->Http->Request (Context->Http, &Context->RequestToken);\r
1157 if (EFI_ERROR (Status)) {\r
1158 goto Error;\r
1159 }\r
1160\r
1161 Status = WaitForCompletion (Context, &gRequestCallbackComplete);\r
1162 if (EFI_ERROR (Status)) {\r
1163 Context->Http->Cancel (Context->Http, &Context->RequestToken);\r
1164 }\r
1165\r
1166Error:\r
1167 SHELL_FREE_NON_NULL (RequestHeader[HdrHost].FieldValue);\r
1168 if (Context->RequestToken.Event) {\r
1169 gBS->CloseEvent (Context->RequestToken.Event);\r
1170 ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));\r
1171 }\r
1172\r
1173 return Status;\r
1174}\r
1175\r
1176/**\r
1177 Update the progress of a file download\r
1178 This procedure is called each time a new HTTP body portion is received.\r
1179\r
1180 @param[in] Context HTTP download context.\r
1181 @param[in] DownloadLen Portion size, in bytes.\r
1182 @param[in] Buffer The pointer to the parsed buffer.\r
1183\r
1184 @retval EFI_SUCCESS Portion saved.\r
1185 @retval Other Error saving the portion.\r
1186**/\r
1187STATIC\r
1188EFI_STATUS\r
1189EFIAPI\r
1190SavePortion (\r
1191 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1192 IN UINTN DownloadLen,\r
1193 IN CHAR8 *Buffer\r
1194 )\r
1195{\r
1196 CHAR16 Progress[HTTP_PROGRESS_MESSAGE_SIZE];\r
1197 UINTN NbOfKb;\r
1198 UINTN Index;\r
1199 UINTN LastStep;\r
1200 UINTN Step;\r
1201 EFI_STATUS Status;\r
1202\r
1203 LastStep = 0;\r
1204 Step = 0;\r
1205\r
1206 ShellSetFilePosition (mFileHandle, Context->LastReportedNbOfBytes);\r
1207 Status = ShellWriteFile (mFileHandle, &DownloadLen, Buffer);\r
1208 if (EFI_ERROR (Status)) {\r
1209 if (Context->ContentDownloaded > 0) {\r
1210 PRINT_HII (STRING_TOKEN (STR_GEN_CRLF), NULL);\r
1211 }\r
1212\r
1213 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_WRITE), mLocalFilePath, Status);\r
1214 return Status;\r
1215 }\r
1216\r
1217 if (Context->ContentDownloaded == 0) {\r
1218 ShellPrintEx (-1, -1, L"%s 0 Kb", HTTP_PROGR_FRAME);\r
1219 }\r
1220\r
1221 Context->ContentDownloaded += DownloadLen;\r
1222 NbOfKb = Context->ContentDownloaded >> 10;\r
1223\r
1224 Progress[0] = L'\0';\r
1225 if (Context->ContentLength) {\r
1226 LastStep = (Context->LastReportedNbOfBytes * HTTP_PROGRESS_SLIDER_STEPS) /\r
1227 Context->ContentLength;\r
1228 Step = (Context->ContentDownloaded * HTTP_PROGRESS_SLIDER_STEPS) /\r
1229 Context->ContentLength;\r
1230 }\r
1231\r
1232 Context->LastReportedNbOfBytes = Context->ContentDownloaded;\r
1233\r
1234 if (Step <= LastStep) {\r
1235 if (!Context->ContentLength) {\r
1236 //\r
1237 // Update downloaded size, there is no length info available.\r
1238 //\r
1239 ShellPrintEx (-1, -1, L"%s", HTTP_KB);\r
1240 ShellPrintEx (-1, -1, L"%7d Kb", NbOfKb);\r
1241 }\r
1242\r
1243 return EFI_SUCCESS;\r
1244 }\r
1245\r
1246 ShellPrintEx (-1, -1, L"%s", HTTP_PROGRESS_DEL);\r
1247\r
1248 Status = StrCpyS (Progress, HTTP_PROGRESS_MESSAGE_SIZE, HTTP_PROGR_FRAME);\r
1249 if (EFI_ERROR (Status)) {\r
1250 return Status;\r
1251 }\r
1252\r
1253 for (Index = 1; Index < Step; Index++) {\r
1254 Progress[Index] = L'=';\r
1255 }\r
1256\r
1257 if (Step) {\r
1258 Progress[Step] = L'>';\r
1259 }\r
1260\r
1261 UnicodeSPrint (\r
1262 Progress + (sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) - 1,\r
1263 sizeof (Progress) - sizeof (HTTP_PROGR_FRAME),\r
1264 L" %7d Kb",\r
1265 NbOfKb\r
1266 );\r
1267\r
1268\r
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
1289 IN CHAR8 *Location,\r
1290 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1291 IN CHAR16 *DownloadUrl\r
1292 )\r
1293{\r
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
1307 IsAbEmptyUrl = FALSE;\r
1308 FirstStep = 0;\r
1309\r
1310 StringSize = (AsciiStrSize (Location) * sizeof (CHAR16));\r
1311 Url = AllocateZeroPool (StringSize);\r
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
1323 goto Error;\r
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
1350 Idx = 0;\r
1351 Walker = Location;\r
1352\r
1353 for (Step = FirstStep; Step < 2; Step++) {\r
1354 for (; *Walker != '/' && *Walker != '\0'; Walker++) {\r
1355 Idx++;\r
1356 }\r
1357\r
1358 if (!Step) {\r
1359 //\r
1360 // Skip "//"\r
1361 //\r
1362 Idx += 2;\r
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
1378 Location += Idx;\r
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
1424 StringSize = AsciiStrSize (Location) * sizeof (CHAR16);\r
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
1463 IN HTTP_BODY_PARSE_EVENT EventType,\r
1464 IN CHAR8 *Data,\r
1465 IN UINTN Length,\r
1466 IN VOID *Context\r
1467 )\r
1468{\r
1469 if ((Data == NULL)\r
1470 || (EventType == BodyParseEventOnComplete)\r
1471 || (Context == NULL))\r
1472 {\r
1473 return EFI_SUCCESS;\r
1474 }\r
1475\r
1476 return SavePortion (Context, Length, Data);\r
1477}\r
1478\r
1479\r
1480/**\r
1481 Get HTTP server response and collect the whole body as a file.\r
1482 Set appropriate status in Context (REQ_OK, REQ_REPEAT, REQ_ERROR).\r
1483 Note that even if HTTP server returns an error code, it might send\r
1484 the body as well. This body will be collected in the resultant file.\r
1485\r
1486 @param[in] Context A pointer to the HTTP download context.\r
1487 @param[in] DownloadUrl A pointer to the fully qualified URL to download.\r
1488\r
1489 @retval EFI_SUCCESS Valid file. Body successfully collected.\r
1490 @retval EFI_HTTP_ERROR Response is a valid HTTP response, but the\r
1491 HTTP server\r
1492 indicated an error (HTTP code >= 400).\r
1493 Response body MAY contain full\r
1494 HTTP server response.\r
1495 @retval Others Error getting the reponse from the HTTP server.\r
1496 Response body is not collected.\r
1497**/\r
1498STATIC\r
1499EFI_STATUS\r
1500GetResponse (\r
1501 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1502 IN CHAR16 *DownloadUrl\r
1503 )\r
1504{\r
1505 EFI_HTTP_RESPONSE_DATA ResponseData;\r
1506 EFI_HTTP_MESSAGE ResponseMessage;\r
1507 EFI_HTTP_HEADER *Header;\r
1508 EFI_STATUS Status;\r
1509 VOID *MsgParser;\r
1510 EFI_TIME StartTime;\r
1511 EFI_TIME EndTime;\r
1512 CONST CHAR16 *Desc;\r
1513 UINTN ElapsedSeconds;\r
1514 BOOLEAN IsTrunked;\r
1515 BOOLEAN CanMeasureTime;\r
1516\r
1517 ZeroMem (&ResponseData, sizeof (ResponseData));\r
1518 ZeroMem (&ResponseMessage, sizeof (ResponseMessage));\r
1519 ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));\r
1520 IsTrunked = FALSE;\r
1521\r
1522 ResponseMessage.Body = Context->Buffer;\r
1523 Context->ResponseToken.Status = EFI_SUCCESS;\r
1524 Context->ResponseToken.Message = &ResponseMessage;\r
1525 Context->ContentLength = 0;\r
1526 Context->Status = REQ_OK;\r
aecfbc81 1527 Status = EFI_SUCCESS;\r
d8ab884f
VO
1528 MsgParser = NULL;\r
1529 ResponseData.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;\r
1530 ResponseMessage.Data.Response = &ResponseData;\r
1531 Context->ResponseToken.Event = NULL;\r
1532 CanMeasureTime = FALSE;\r
1533 if (Context->Flags & DL_FLAG_TIME) {\r
1534 ZeroMem (&StartTime, sizeof (StartTime));\r
1535 CanMeasureTime = !EFI_ERROR (gRT->GetTime (&StartTime, NULL));\r
1536 }\r
1537\r
1538 do {\r
1539 SHELL_FREE_NON_NULL (ResponseMessage.Headers);\r
1540 ResponseMessage.HeaderCount = 0;\r
1541 gResponseCallbackComplete = FALSE;\r
1542 ResponseMessage.BodyLength = Context->BufferSize;\r
1543\r
1544 if (ShellGetExecutionBreakFlag ()) {\r
1545 Status = EFI_ABORTED;\r
1546 break;\r
1547 }\r
1548\r
1549 if (!Context->ContentDownloaded && !Context->ResponseToken.Event) {\r
1550 Status = gBS->CreateEvent (\r
1551 EVT_NOTIFY_SIGNAL,\r
1552 TPL_CALLBACK,\r
1553 ResponseCallback,\r
1554 Context,\r
1555 &Context->ResponseToken.Event\r
1556 );\r
1557 ASSERT_EFI_ERROR (Status);\r
1558 } else {\r
1559 ResponseMessage.Data.Response = NULL;\r
1560 }\r
1561\r
1562 if (EFI_ERROR (Status)) {\r
1563 break;\r
1564 }\r
1565\r
1566 Status = Context->Http->Response (Context->Http, &Context->ResponseToken);\r
1567 if (EFI_ERROR (Status)) {\r
1568 break;\r
1569 }\r
1570\r
1571 Status = WaitForCompletion (Context, &gResponseCallbackComplete);\r
1572 if (EFI_ERROR (Status) && ResponseMessage.HeaderCount) {\r
1573 Status = EFI_SUCCESS;\r
1574 }\r
1575\r
1576 if (EFI_ERROR (Status)) {\r
1577 Context->Http->Cancel (Context->Http, &Context->ResponseToken);\r
1578 break;\r
1579 }\r
1580\r
1581 if (!Context->ContentDownloaded) {\r
1582 if (NEED_REDIRECTION (ResponseData.StatusCode)) {\r
1583 //\r
1584 // Need to repeat the request with new Location (server redirected).\r
1585 //\r
1586 Context->Status = REQ_NEED_REPEAT;\r
1587\r
1588 Header = HttpFindHeader (\r
1589 ResponseMessage.HeaderCount,\r
1590 ResponseMessage.Headers,\r
1591 "Location"\r
1592 );\r
1593 if (Header) {\r
1594 Status = SetHostURI (Header->FieldValue, Context, DownloadUrl);\r
1595 if (Status == EFI_NO_MAPPING) {\r
1596 PRINT_HII (\r
1597 STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),\r
1598 Context->ServerAddrAndProto,\r
1599 L"Recursive HTTP server relocation",\r
1600 Context->Uri\r
1601 );\r
1602 }\r
1603 } else {\r
1604 //\r
1605 // Bad reply from the server. Server must specify the location.\r
1606 // Indicate that resource was not found, and no body collected.\r
1607 //\r
1608 Status = EFI_NOT_FOUND;\r
1609 }\r
1610\r
1611 Context->Http->Cancel (Context->Http, &Context->ResponseToken);\r
1612 break;\r
1613 }\r
1614\r
1615 //\r
1616 // Init message-body parser by header information.\r
1617 //\r
1618 if (!MsgParser) {\r
1619 Status = HttpInitMsgParser (\r
1620 ResponseMessage.Data.Request->Method,\r
1621 ResponseData.StatusCode,\r
1622 ResponseMessage.HeaderCount,\r
1623 ResponseMessage.Headers,\r
1624 ParseMsg,\r
1625 Context,\r
1626 &MsgParser\r
1627 );\r
1628 if (EFI_ERROR (Status)) {\r
1629 break;\r
1630 }\r
1631 }\r
1632\r
1633 //\r
1634 // If it is a trunked message, rely on the parser.\r
1635 //\r
1636 Header = HttpFindHeader (\r
1637 ResponseMessage.HeaderCount,\r
1638 ResponseMessage.Headers,\r
1639 "Transfer-Encoding"\r
1640 );\r
1641 IsTrunked = (Header && !AsciiStrCmp (Header->FieldValue, "chunked"));\r
1642\r
1643 HttpGetEntityLength (MsgParser, &Context->ContentLength);\r
1644\r
1645 if (ResponseData.StatusCode >= HTTP_STATUS_400_BAD_REQUEST\r
1646 && (ResponseData.StatusCode != HTTP_STATUS_308_PERMANENT_REDIRECT))\r
1647 {\r
1648 //\r
1649 // Server reported an error via Response code.\r
1650 // Collect the body if any.\r
1651 //\r
1652 if (!gHttpError) {\r
1653 gHttpError = TRUE;\r
1654\r
1655 Desc = ErrStatusDesc[ResponseData.StatusCode -\r
1656 HTTP_STATUS_400_BAD_REQUEST];\r
1657 PRINT_HII (\r
1658 STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),\r
1659 Context->ServerAddrAndProto,\r
1660 Desc,\r
1661 Context->Uri\r
1662 );\r
1663\r
1664 //\r
1665 // This gives an RFC HTTP error.\r
1666 //\r
1667 Context->Status = ShellStrToUintn (Desc);\r
1668 Status = ENCODE_ERROR (Context->Status);\r
1669 }\r
1670 }\r
1671 }\r
1672\r
1673 //\r
1674 // Do NOT try to parse an empty body.\r
1675 //\r
1676 if (ResponseMessage.BodyLength || IsTrunked) {\r
1677 Status = HttpParseMessageBody (\r
1678 MsgParser,\r
1679 ResponseMessage.BodyLength,\r
1680 ResponseMessage.Body\r
1681 );\r
1682 }\r
1683 } while (!HttpIsMessageComplete (MsgParser)\r
1684 && !EFI_ERROR (Status)\r
1685 && ResponseMessage.BodyLength);\r
1686\r
1687 if (Context->Status != REQ_NEED_REPEAT\r
1688 && Status == EFI_SUCCESS\r
1689 && CanMeasureTime)\r
1690 {\r
1691 if (!EFI_ERROR (gRT->GetTime (&EndTime, NULL))) {\r
1692 ElapsedSeconds = EfiTimeToEpoch (&EndTime) - EfiTimeToEpoch (&StartTime);\r
1693 Print (\r
1694 L",%a%Lus\n",\r
1695 ElapsedSeconds ? " " : " < ",\r
1696 ElapsedSeconds > 1 ? (UINT64)ElapsedSeconds : 1\r
1697 );\r
1698 }\r
1699 }\r
1700\r
1701 SHELL_FREE_NON_NULL (MsgParser);\r
1702 if (Context->ResponseToken.Event) {\r
1703 gBS->CloseEvent (Context->ResponseToken.Event);\r
1704 ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));\r
1705 }\r
1706\r
1707 return Status;\r
1708}\r
1709\r
1710/**\r
1711 Worker function that downloads the data of a file from an HTTP server given\r
1712 the path of the file and its size.\r
1713\r
1714 @param[in] Context A pointer to the HTTP download context.\r
1715 @param[in] ControllerHandle The handle of the network interface controller\r
1716 @param[in] NicName NIC name\r
1717\r
1718 @retval EFI_SUCCESS The file was downloaded.\r
1719 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
1720 #return EFI_HTTP_ERROR The server returned a valid HTTP error.\r
1721 Examine the mLocalFilePath file\r
1722 to get error body.\r
1723 @retval Others The downloading of the file from the server\r
1724 failed.\r
1725**/\r
1726STATIC\r
1727EFI_STATUS\r
1728DownloadFile (\r
1729 IN HTTP_DOWNLOAD_CONTEXT *Context,\r
1730 IN EFI_HANDLE ControllerHandle,\r
1731 IN CHAR16 *NicName\r
1732 )\r
1733{\r
1734 EFI_STATUS Status;\r
1735 CHAR16 *DownloadUrl;\r
1736 UINTN UrlSize;\r
1737 EFI_HANDLE HttpChildHandle;\r
1738\r
1739 ASSERT (Context);\r
1740 if (Context == NULL) {\r
1741 return EFI_INVALID_PARAMETER;\r
1742 }\r
1743\r
1744 DownloadUrl = NULL;\r
1745 HttpChildHandle = NULL;\r
1746\r
1747 Context->Buffer = AllocatePool (Context->BufferSize);\r
1748 if (Context->Buffer == NULL) {\r
1749 Status = EFI_OUT_OF_RESOURCES;\r
1750 goto ON_EXIT;\r
1751 }\r
1752\r
1753 //\r
1754 // Open the file.\r
1755 //\r
1756 if (!EFI_ERROR (ShellFileExists (mLocalFilePath))) {\r
1757 ShellDeleteFileByName (mLocalFilePath);\r
1758 }\r
1759\r
1760 Status = ShellOpenFileByName (\r
1761 mLocalFilePath,\r
1762 &mFileHandle,\r
1763 EFI_FILE_MODE_CREATE |\r
1764 EFI_FILE_MODE_WRITE |\r
1765 EFI_FILE_MODE_READ,\r
1766 0\r
1767 );\r
1768 if (EFI_ERROR (Status)) {\r
1769 PRINT_HII_APP (STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), mLocalFilePath);\r
1770 goto ON_EXIT;\r
1771 }\r
1772\r
1773 do {\r
1774 SHELL_FREE_NON_NULL (DownloadUrl);\r
1775\r
1776 CLOSE_HTTP_HANDLE (ControllerHandle, HttpChildHandle);\r
1777\r
1778 Status = CreateServiceChildAndOpenProtocol (\r
1779 ControllerHandle,\r
1780 &gEfiHttpServiceBindingProtocolGuid,\r
1781 &gEfiHttpProtocolGuid,\r
1782 &HttpChildHandle,\r
1783 (VOID**)&Context->Http\r
1784 );\r
1785\r
1786 if (EFI_ERROR (Status)) {\r
1787 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_OPEN_PROTOCOL), NicName, Status);\r
1788 goto ON_EXIT;\r
1789 }\r
1790\r
1791 Status = Context->Http->Configure (Context->Http, &Context->HttpConfigData);\r
1792 if (EFI_ERROR (Status)) {\r
1793 PRINT_HII (STRING_TOKEN (STR_HTTP_ERR_CONFIGURE), NicName, Status);\r
1794 goto ON_EXIT;\r
1795 }\r
1796\r
1797 UrlSize = 0;\r
1798 DownloadUrl = StrnCatGrow (\r
1799 &DownloadUrl,\r
1800 &UrlSize,\r
1801 Context->ServerAddrAndProto,\r
1802 StrLen (Context->ServerAddrAndProto)\r
1803 );\r
1804 if (Context->Uri[0] != L'/') {\r
1805 DownloadUrl = StrnCatGrow (\r
1806 &DownloadUrl,\r
1807 &UrlSize,\r
1808 L"/",\r
1809 StrLen (Context->ServerAddrAndProto)\r
1810 );\r
1811 }\r
1812\r
1813 DownloadUrl = StrnCatGrow (\r
1814 &DownloadUrl,\r
1815 &UrlSize,\r
1816 Context->Uri,\r
1817 StrLen (Context->Uri));\r
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
1831\r
1832 } while (Context->Status == REQ_NEED_REPEAT);\r
1833\r
1834 if (Context->Status) {\r
1835 Status = ENCODE_ERROR (Context->Status);\r
1836 }\r
1837\r
1838ON_EXIT:\r
1839 //\r
1840 // Close the file.\r
1841 //\r
1842 if (mFileHandle != NULL) {\r
1843 if (EFI_ERROR (Status) && !(Context->Flags & DL_FLAG_KEEP_BAD)) {\r
1844 ShellDeleteFile (&mFileHandle);\r
1845 } else {\r
1846 ShellCloseFile (&mFileHandle);\r
1847 }\r
1848 }\r
1849\r
1850 SHELL_FREE_NON_NULL (DownloadUrl);\r
1851 SHELL_FREE_NON_NULL (Context->Buffer);\r
1852\r
1853 CLOSE_HTTP_HANDLE (ControllerHandle, HttpChildHandle);\r
1854\r
1855 return Status;\r
1856}\r
1857\r
1858/**\r
1859 Retrive HII package list from ImageHandle and publish to HII database.\r
1860\r
1861 @param[in] ImageHandle The image handle of the process.\r
1862\r
1863 @retval HII handle.\r
1864**/\r
1865EFI_HII_HANDLE\r
1866InitializeHiiPackage (\r
1867 IN EFI_HANDLE ImageHandle\r
1868 )\r
1869{\r
1870 EFI_STATUS Status;\r
1871 EFI_HII_PACKAGE_LIST_HEADER *PackageList;\r
1872 EFI_HII_HANDLE HiiHandle;\r
1873\r
1874 //\r
1875 // Retrieve HII package list from ImageHandle.\r
1876 //\r
1877 Status = gBS->OpenProtocol (\r
1878 ImageHandle,\r
1879 &gEfiHiiPackageListProtocolGuid,\r
1880 (VOID **)&PackageList,\r
1881 ImageHandle,\r
1882 NULL,\r
1883 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1884 );\r
1885 ASSERT_EFI_ERROR (Status);\r
1886 if (EFI_ERROR (Status)) {\r
1887 return NULL;\r
1888 }\r
1889\r
1890 //\r
1891 // Publish HII package list to HII Database.\r
1892 //\r
1893 Status = gHiiDatabase->NewPackageList (\r
1894 gHiiDatabase,\r
1895 PackageList,\r
1896 NULL,\r
1897 &HiiHandle\r
1898 );\r
1899 ASSERT_EFI_ERROR (Status);\r
1900 if (EFI_ERROR (Status)) {\r
1901 return NULL;\r
1902 }\r
1903\r
1904 return HiiHandle;\r
1905}\r