Http.c 53 KB

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