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