]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c
NetworkPkg: Apply uncrustify changes
[mirror_edk2.git] / NetworkPkg / Library / DxeHttpLib / DxeHttpLib.c
CommitLineData
f9a14916
JW
1/** @file\r
2 This library is used to share code between UEFI network stack modules.\r
3 It provides the helper routines to parse the HTTP message byte stream.\r
4\r
62cdd1f5 5Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>\r
40c4cd54 6(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR>\r
9d510e61 7SPDX-License-Identifier: BSD-2-Clause-Patent\r
f9a14916
JW
8\r
9**/\r
10\r
558b99a6 11#include "DxeHttpLib.h"\r
f9a14916 12\r
f9a14916
JW
13/**\r
14 Decode a percent-encoded URI component to the ASCII character.\r
d1102dba
LG
15\r
16 Decode the input component in Buffer according to RFC 3986. The caller is responsible to make\r
f9a14916 17 sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))\r
d1102dba 18 in bytes.\r
f9a14916
JW
19\r
20 @param[in] Buffer The pointer to a percent-encoded URI component.\r
21 @param[in] BufferLength Length of Buffer in bytes.\r
22 @param[out] ResultBuffer Point to the buffer to store the decode result.\r
23 @param[out] ResultLength Length of decoded string in ResultBuffer in bytes.\r
24\r
25 @retval EFI_SUCCESS Successfully decoded the URI.\r
26 @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string.\r
d1102dba 27\r
f9a14916
JW
28**/\r
29EFI_STATUS\r
30EFIAPI\r
31UriPercentDecode (\r
d1050b9d
MK
32 IN CHAR8 *Buffer,\r
33 IN UINT32 BufferLength,\r
34 OUT CHAR8 *ResultBuffer,\r
35 OUT UINT32 *ResultLength\r
f9a14916
JW
36 )\r
37{\r
d1050b9d
MK
38 UINTN Index;\r
39 UINTN Offset;\r
40 CHAR8 HexStr[3];\r
f9a14916 41\r
d1050b9d 42 if ((Buffer == NULL) || (BufferLength == 0) || (ResultBuffer == NULL)) {\r
f9a14916
JW
43 return EFI_INVALID_PARAMETER;\r
44 }\r
d1102dba 45\r
d1050b9d
MK
46 Index = 0;\r
47 Offset = 0;\r
f9a14916
JW
48 HexStr[2] = '\0';\r
49 while (Index < BufferLength) {\r
50 if (Buffer[Index] == '%') {\r
d1050b9d
MK
51 if ((Index + 1 >= BufferLength) || (Index + 2 >= BufferLength) ||\r
52 !NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2]))\r
53 {\r
f9a14916
JW
54 return EFI_INVALID_PARAMETER;\r
55 }\r
d1050b9d
MK
56\r
57 HexStr[0] = Buffer[Index+1];\r
58 HexStr[1] = Buffer[Index+2];\r
59 ResultBuffer[Offset] = (CHAR8)AsciiStrHexToUintn (HexStr);\r
60 Index += 3;\r
f9a14916
JW
61 } else {\r
62 ResultBuffer[Offset] = Buffer[Index];\r
63 Index++;\r
64 }\r
d1050b9d 65\r
f9a14916
JW
66 Offset++;\r
67 }\r
68\r
d1050b9d 69 *ResultLength = (UINT32)Offset;\r
d1102dba 70\r
f9a14916
JW
71 return EFI_SUCCESS;\r
72}\r
73\r
74/**\r
8a1c4a8c 75 This function return the updated state according to the input state and next character of\r
f9a14916
JW
76 the authority.\r
77\r
78 @param[in] Char Next character.\r
79 @param[in] State Current value of the parser state machine.\r
d1102dba
LG
80 @param[in] IsRightBracket TRUE if there is an sign ']' in the authority component and\r
81 indicates the next part is ':' before Port.\r
f9a14916
JW
82\r
83 @return Updated state value.\r
84**/\r
85HTTP_URL_PARSE_STATE\r
86NetHttpParseAuthorityChar (\r
d1050b9d
MK
87 IN CHAR8 Char,\r
88 IN HTTP_URL_PARSE_STATE State,\r
89 IN BOOLEAN *IsRightBracket\r
f9a14916
JW
90 )\r
91{\r
f9a14916
JW
92 //\r
93 // RFC 3986:\r
94 // The authority component is preceded by a double slash ("//") and is\r
95 // terminated by the next slash ("/"), question mark ("?"), or number\r
96 // sign ("#") character, or by the end of the URI.\r
97 //\r
d1050b9d 98 if ((Char == ' ') || (Char == '\r') || (Char == '\n')) {\r
f9a14916
JW
99 return UrlParserStateMax;\r
100 }\r
101\r
102 //\r
103 // authority = [ userinfo "@" ] host [ ":" port ]\r
104 //\r
105 switch (State) {\r
d1050b9d
MK
106 case UrlParserUserInfo:\r
107 if (Char == '@') {\r
108 return UrlParserHostStart;\r
109 }\r
f9a14916 110\r
d1050b9d 111 break;\r
d1102dba 112\r
d1050b9d
MK
113 case UrlParserHost:\r
114 case UrlParserHostStart:\r
115 if (Char == '[') {\r
116 return UrlParserHostIpv6;\r
117 }\r
d1102dba 118\r
d1050b9d
MK
119 if (Char == ':') {\r
120 return UrlParserPortStart;\r
121 }\r
d1102dba 122\r
d1050b9d 123 return UrlParserHost;\r
d1102dba 124\r
d1050b9d
MK
125 case UrlParserHostIpv6:\r
126 if (Char == ']') {\r
127 *IsRightBracket = TRUE;\r
128 }\r
129\r
130 if ((Char == ':') && *IsRightBracket) {\r
131 return UrlParserPortStart;\r
132 }\r
d1102dba 133\r
d1050b9d
MK
134 return UrlParserHostIpv6;\r
135\r
136 case UrlParserPort:\r
137 case UrlParserPortStart:\r
138 return UrlParserPort;\r
f9a14916 139\r
d1050b9d
MK
140 default:\r
141 break;\r
f9a14916
JW
142 }\r
143\r
144 return State;\r
145}\r
146\r
147/**\r
148 This function parse the authority component of the input URL and update the parser.\r
149\r
150 @param[in] Url The pointer to a HTTP URL string.\r
151 @param[in] FoundAt TRUE if there is an at sign ('@') in the authority component.\r
152 @param[in, out] UrlParser Pointer to the buffer of the parse result.\r
153\r
154 @retval EFI_SUCCESS Successfully parse the authority.\r
4a6f440f 155 @retval EFI_INVALID_PARAMETER The Url is invalid to parse the authority component.\r
f9a14916
JW
156\r
157**/\r
158EFI_STATUS\r
159NetHttpParseAuthority (\r
d1050b9d
MK
160 IN CHAR8 *Url,\r
161 IN BOOLEAN FoundAt,\r
162 IN OUT HTTP_URL_PARSER *UrlParser\r
f9a14916
JW
163 )\r
164{\r
165 CHAR8 *Char;\r
166 CHAR8 *Authority;\r
167 UINT32 Length;\r
168 HTTP_URL_PARSE_STATE State;\r
169 UINT32 Field;\r
170 UINT32 OldField;\r
e91cc29d 171 BOOLEAN IsrightBracket;\r
d1102dba 172\r
f9a14916
JW
173 ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);\r
174\r
175 //\r
176 // authority = [ userinfo "@" ] host [ ":" port ]\r
177 //\r
178 if (FoundAt) {\r
179 State = UrlParserUserInfo;\r
180 } else {\r
181 State = UrlParserHost;\r
182 }\r
183\r
e91cc29d 184 IsrightBracket = FALSE;\r
d1050b9d
MK
185 Field = HTTP_URI_FIELD_MAX;\r
186 OldField = Field;\r
187 Authority = Url + UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Offset;\r
188 Length = UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Length;\r
f9a14916 189 for (Char = Authority; Char < Authority + Length; Char++) {\r
e91cc29d 190 State = NetHttpParseAuthorityChar (*Char, State, &IsrightBracket);\r
f9a14916 191 switch (State) {\r
d1050b9d
MK
192 case UrlParserStateMax:\r
193 return EFI_INVALID_PARAMETER;\r
f9a14916 194\r
d1050b9d
MK
195 case UrlParserHostStart:\r
196 case UrlParserPortStart:\r
197 continue;\r
f9a14916 198\r
d1050b9d
MK
199 case UrlParserUserInfo:\r
200 Field = HTTP_URI_FIELD_USERINFO;\r
201 break;\r
d1102dba 202\r
d1050b9d
MK
203 case UrlParserHost:\r
204 Field = HTTP_URI_FIELD_HOST;\r
205 break;\r
e91cc29d 206\r
d1050b9d
MK
207 case UrlParserHostIpv6:\r
208 Field = HTTP_URI_FIELD_HOST;\r
209 break;\r
d1102dba 210\r
d1050b9d
MK
211 case UrlParserPort:\r
212 Field = HTTP_URI_FIELD_PORT;\r
213 break;\r
f9a14916 214\r
d1050b9d
MK
215 default:\r
216 ASSERT (FALSE);\r
f9a14916
JW
217 }\r
218\r
219 //\r
220 // Field not changed, count the length.\r
221 //\r
222 ASSERT (Field < HTTP_URI_FIELD_MAX);\r
223 if (Field == OldField) {\r
224 UrlParser->FieldData[Field].Length++;\r
225 continue;\r
226 }\r
227\r
228 //\r
229 // New field start\r
230 //\r
d1050b9d
MK
231 UrlParser->FieldBitMap |= BIT (Field);\r
232 UrlParser->FieldData[Field].Offset = (UINT32)(Char - Url);\r
f9a14916 233 UrlParser->FieldData[Field].Length = 1;\r
d1050b9d 234 OldField = Field;\r
f9a14916
JW
235 }\r
236\r
237 return EFI_SUCCESS;\r
238}\r
239\r
240/**\r
8a1c4a8c 241 This function return the updated state according to the input state and next character of a URL.\r
f9a14916
JW
242\r
243 @param[in] Char Next character.\r
244 @param[in] State Current value of the parser state machine.\r
245\r
246 @return Updated state value.\r
247\r
248**/\r
249HTTP_URL_PARSE_STATE\r
250NetHttpParseUrlChar (\r
d1050b9d
MK
251 IN CHAR8 Char,\r
252 IN HTTP_URL_PARSE_STATE State\r
f9a14916
JW
253 )\r
254{\r
d1050b9d 255 if ((Char == ' ') || (Char == '\r') || (Char == '\n')) {\r
f9a14916
JW
256 return UrlParserStateMax;\r
257 }\r
d1102dba 258\r
f9a14916
JW
259 //\r
260 // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]\r
d1102dba 261 //\r
f9a14916 262 // Request-URI = "*" | absolute-URI | path-absolute | authority\r
d1102dba 263 //\r
f9a14916
JW
264 // absolute-URI = scheme ":" hier-part [ "?" query ]\r
265 // path-absolute = "/" [ segment-nz *( "/" segment ) ]\r
266 // authority = [ userinfo "@" ] host [ ":" port ]\r
267 //\r
268 switch (State) {\r
d1050b9d
MK
269 case UrlParserUrlStart:\r
270 if ((Char == '*') || (Char == '/')) {\r
271 return UrlParserPath;\r
272 }\r
f9a14916 273\r
d1050b9d 274 return UrlParserScheme;\r
f9a14916 275\r
d1050b9d
MK
276 case UrlParserScheme:\r
277 if (Char == ':') {\r
278 return UrlParserSchemeColon;\r
279 }\r
f9a14916 280\r
d1050b9d 281 break;\r
f9a14916 282\r
d1050b9d
MK
283 case UrlParserSchemeColon:\r
284 if (Char == '/') {\r
285 return UrlParserSchemeColonSlash;\r
286 }\r
f9a14916 287\r
d1050b9d 288 break;\r
f9a14916 289\r
d1050b9d
MK
290 case UrlParserSchemeColonSlash:\r
291 if (Char == '/') {\r
292 return UrlParserSchemeColonSlashSlash;\r
293 }\r
f9a14916 294\r
d1050b9d
MK
295 break;\r
296\r
297 case UrlParserAtInAuthority:\r
298 if (Char == '@') {\r
299 return UrlParserStateMax;\r
300 }\r
f9a14916 301\r
d1050b9d
MK
302 case UrlParserAuthority:\r
303 case UrlParserSchemeColonSlashSlash:\r
304 if (Char == '@') {\r
305 return UrlParserAtInAuthority;\r
306 }\r
307\r
308 if (Char == '/') {\r
309 return UrlParserPath;\r
310 }\r
311\r
312 if (Char == '?') {\r
313 return UrlParserQueryStart;\r
314 }\r
315\r
316 if (Char == '#') {\r
317 return UrlParserFragmentStart;\r
318 }\r
319\r
320 return UrlParserAuthority;\r
321\r
322 case UrlParserPath:\r
323 if (Char == '?') {\r
324 return UrlParserQueryStart;\r
325 }\r
326\r
327 if (Char == '#') {\r
328 return UrlParserFragmentStart;\r
329 }\r
330\r
331 break;\r
332\r
333 case UrlParserQuery:\r
334 case UrlParserQueryStart:\r
335 if (Char == '#') {\r
336 return UrlParserFragmentStart;\r
337 }\r
d1102dba 338\r
d1050b9d
MK
339 return UrlParserQuery;\r
340\r
341 case UrlParserFragmentStart:\r
342 return UrlParserFragment;\r
343\r
344 default:\r
345 break;\r
f9a14916
JW
346 }\r
347\r
348 return State;\r
349}\r
d1050b9d 350\r
f9a14916
JW
351/**\r
352 Create a URL parser for the input URL string.\r
353\r
354 This function will parse and dereference the input HTTP URL into it components. The original\r
355 content of the URL won't be modified and the result will be returned in UrlParser, which can\r
356 be used in other functions like NetHttpUrlGetHostName().\r
357\r
358 @param[in] Url The pointer to a HTTP URL string.\r
359 @param[in] Length Length of Url in bytes.\r
360 @param[in] IsConnectMethod Whether the Url is used in HTTP CONNECT method or not.\r
361 @param[out] UrlParser Pointer to the returned buffer to store the parse result.\r
362\r
363 @retval EFI_SUCCESS Successfully dereferenced the HTTP URL.\r
364 @retval EFI_INVALID_PARAMETER UrlParser is NULL or Url is not a valid HTTP URL.\r
365 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
366\r
367**/\r
368EFI_STATUS\r
369EFIAPI\r
370HttpParseUrl (\r
d1050b9d
MK
371 IN CHAR8 *Url,\r
372 IN UINT32 Length,\r
373 IN BOOLEAN IsConnectMethod,\r
374 OUT VOID **UrlParser\r
f9a14916
JW
375 )\r
376{\r
377 HTTP_URL_PARSE_STATE State;\r
378 CHAR8 *Char;\r
379 UINT32 Field;\r
380 UINT32 OldField;\r
381 BOOLEAN FoundAt;\r
382 EFI_STATUS Status;\r
383 HTTP_URL_PARSER *Parser;\r
f5168b84
JW
384\r
385 Parser = NULL;\r
d1102dba 386\r
d1050b9d 387 if ((Url == NULL) || (Length == 0) || (UrlParser == NULL)) {\r
f9a14916
JW
388 return EFI_INVALID_PARAMETER;\r
389 }\r
390\r
391 Parser = AllocateZeroPool (sizeof (HTTP_URL_PARSER));\r
392 if (Parser == NULL) {\r
393 return EFI_OUT_OF_RESOURCES;\r
394 }\r
d1102dba 395\r
f9a14916
JW
396 if (IsConnectMethod) {\r
397 //\r
398 // According to RFC 2616, the authority form is only used by the CONNECT method.\r
399 //\r
400 State = UrlParserAuthority;\r
401 } else {\r
402 State = UrlParserUrlStart;\r
403 }\r
404\r
d1050b9d 405 Field = HTTP_URI_FIELD_MAX;\r
f9a14916 406 OldField = Field;\r
d1050b9d 407 FoundAt = FALSE;\r
f9a14916
JW
408 for (Char = Url; Char < Url + Length; Char++) {\r
409 //\r
24648548 410 // Update state machine according to next char.\r
f9a14916
JW
411 //\r
412 State = NetHttpParseUrlChar (*Char, State);\r
413\r
414 switch (State) {\r
d1050b9d
MK
415 case UrlParserStateMax:\r
416 FreePool (Parser);\r
417 return EFI_INVALID_PARAMETER;\r
d1102dba 418\r
d1050b9d
MK
419 case UrlParserSchemeColon:\r
420 case UrlParserSchemeColonSlash:\r
421 case UrlParserSchemeColonSlashSlash:\r
422 case UrlParserQueryStart:\r
423 case UrlParserFragmentStart:\r
424 //\r
425 // Skip all the delimiting char: "://" "?" "@"\r
426 //\r
427 continue;\r
d1102dba 428\r
d1050b9d
MK
429 case UrlParserScheme:\r
430 Field = HTTP_URI_FIELD_SCHEME;\r
431 break;\r
f9a14916 432\r
d1050b9d
MK
433 case UrlParserAtInAuthority:\r
434 FoundAt = TRUE;\r
435 case UrlParserAuthority:\r
436 Field = HTTP_URI_FIELD_AUTHORITY;\r
437 break;\r
f9a14916 438\r
d1050b9d
MK
439 case UrlParserPath:\r
440 Field = HTTP_URI_FIELD_PATH;\r
441 break;\r
f9a14916 442\r
d1050b9d
MK
443 case UrlParserQuery:\r
444 Field = HTTP_URI_FIELD_QUERY;\r
445 break;\r
f9a14916 446\r
d1050b9d
MK
447 case UrlParserFragment:\r
448 Field = HTTP_URI_FIELD_FRAGMENT;\r
449 break;\r
f9a14916 450\r
d1050b9d
MK
451 default:\r
452 ASSERT (FALSE);\r
f9a14916
JW
453 }\r
454\r
455 //\r
456 // Field not changed, count the length.\r
457 //\r
458 ASSERT (Field < HTTP_URI_FIELD_MAX);\r
459 if (Field == OldField) {\r
460 Parser->FieldData[Field].Length++;\r
461 continue;\r
462 }\r
463\r
464 //\r
465 // New field start\r
466 //\r
d1050b9d
MK
467 Parser->FieldBitMap |= BIT (Field);\r
468 Parser->FieldData[Field].Offset = (UINT32)(Char - Url);\r
f9a14916 469 Parser->FieldData[Field].Length = 1;\r
d1050b9d 470 OldField = Field;\r
f9a14916
JW
471 }\r
472\r
473 //\r
474 // If has authority component, continue to parse the username, host and port.\r
475 //\r
476 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) {\r
477 Status = NetHttpParseAuthority (Url, FoundAt, Parser);\r
478 if (EFI_ERROR (Status)) {\r
f5168b84 479 FreePool (Parser);\r
f9a14916
JW
480 return Status;\r
481 }\r
482 }\r
483\r
484 *UrlParser = Parser;\r
d1102dba 485 return EFI_SUCCESS;\r
f9a14916
JW
486}\r
487\r
488/**\r
489 Get the Hostname from a HTTP URL.\r
490\r
491 This function will return the HostName according to the Url and previous parse result ,and\r
492 it is the caller's responsibility to free the buffer returned in *HostName.\r
493\r
494 @param[in] Url The pointer to a HTTP URL string.\r
495 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
496 @param[out] HostName Pointer to a buffer to store the HostName.\r
497\r
498 @retval EFI_SUCCESS Successfully get the required component.\r
499 @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.\r
500 @retval EFI_NOT_FOUND No hostName component in the URL.\r
501 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
d1102dba 502\r
f9a14916
JW
503**/\r
504EFI_STATUS\r
505EFIAPI\r
506HttpUrlGetHostName (\r
d1050b9d
MK
507 IN CHAR8 *Url,\r
508 IN VOID *UrlParser,\r
509 OUT CHAR8 **HostName\r
f9a14916
JW
510 )\r
511{\r
d1050b9d
MK
512 CHAR8 *Name;\r
513 EFI_STATUS Status;\r
514 UINT32 ResultLength;\r
515 HTTP_URL_PARSER *Parser;\r
f9a14916 516\r
d1050b9d 517 if ((Url == NULL) || (UrlParser == NULL) || (HostName == NULL)) {\r
f9a14916
JW
518 return EFI_INVALID_PARAMETER;\r
519 }\r
520\r
d1050b9d 521 Parser = (HTTP_URL_PARSER *)UrlParser;\r
f9a14916
JW
522\r
523 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
524 return EFI_NOT_FOUND;\r
525 }\r
526\r
527 Name = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);\r
528 if (Name == NULL) {\r
529 return EFI_OUT_OF_RESOURCES;\r
530 }\r
d1102dba 531\r
f9a14916
JW
532 Status = UriPercentDecode (\r
533 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
534 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
535 Name,\r
536 &ResultLength\r
537 );\r
538 if (EFI_ERROR (Status)) {\r
df642d70 539 FreePool (Name);\r
f9a14916
JW
540 return Status;\r
541 }\r
542\r
543 Name[ResultLength] = '\0';\r
d1050b9d 544 *HostName = Name;\r
f9a14916
JW
545 return EFI_SUCCESS;\r
546}\r
547\r
f9a14916
JW
548/**\r
549 Get the IPv4 address from a HTTP URL.\r
550\r
551 This function will return the IPv4 address according to the Url and previous parse result.\r
552\r
553 @param[in] Url The pointer to a HTTP URL string.\r
554 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
555 @param[out] Ip4Address Pointer to a buffer to store the IP address.\r
556\r
557 @retval EFI_SUCCESS Successfully get the required component.\r
558 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid.\r
559 @retval EFI_NOT_FOUND No IPv4 address component in the URL.\r
560 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
d1102dba 561\r
f9a14916
JW
562**/\r
563EFI_STATUS\r
564EFIAPI\r
565HttpUrlGetIp4 (\r
d1050b9d
MK
566 IN CHAR8 *Url,\r
567 IN VOID *UrlParser,\r
568 OUT EFI_IPv4_ADDRESS *Ip4Address\r
f9a14916
JW
569 )\r
570{\r
d1050b9d
MK
571 CHAR8 *Ip4String;\r
572 EFI_STATUS Status;\r
573 UINT32 ResultLength;\r
574 HTTP_URL_PARSER *Parser;\r
d1102dba 575\r
d1050b9d 576 if ((Url == NULL) || (UrlParser == NULL) || (Ip4Address == NULL)) {\r
f9a14916
JW
577 return EFI_INVALID_PARAMETER;\r
578 }\r
579\r
d1050b9d 580 Parser = (HTTP_URL_PARSER *)UrlParser;\r
f9a14916
JW
581\r
582 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
4a6f440f 583 return EFI_NOT_FOUND;\r
f9a14916
JW
584 }\r
585\r
586 Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);\r
587 if (Ip4String == NULL) {\r
588 return EFI_OUT_OF_RESOURCES;\r
589 }\r
d1102dba 590\r
f9a14916
JW
591 Status = UriPercentDecode (\r
592 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,\r
593 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,\r
594 Ip4String,\r
595 &ResultLength\r
596 );\r
597 if (EFI_ERROR (Status)) {\r
df642d70 598 FreePool (Ip4String);\r
f9a14916
JW
599 return Status;\r
600 }\r
601\r
602 Ip4String[ResultLength] = '\0';\r
d1050b9d 603 Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);\r
f9a14916
JW
604 FreePool (Ip4String);\r
605\r
606 return Status;\r
607}\r
608\r
609/**\r
610 Get the IPv6 address from a HTTP URL.\r
611\r
612 This function will return the IPv6 address according to the Url and previous parse result.\r
613\r
614 @param[in] Url The pointer to a HTTP URL string.\r
615 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
616 @param[out] Ip6Address Pointer to a buffer to store the IP address.\r
617\r
618 @retval EFI_SUCCESS Successfully get the required component.\r
619 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid.\r
620 @retval EFI_NOT_FOUND No IPv6 address component in the URL.\r
621 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
d1102dba 622\r
f9a14916
JW
623**/\r
624EFI_STATUS\r
625EFIAPI\r
626HttpUrlGetIp6 (\r
d1050b9d
MK
627 IN CHAR8 *Url,\r
628 IN VOID *UrlParser,\r
629 OUT EFI_IPv6_ADDRESS *Ip6Address\r
f9a14916
JW
630 )\r
631{\r
d1050b9d
MK
632 CHAR8 *Ip6String;\r
633 CHAR8 *Ptr;\r
634 UINT32 Length;\r
635 EFI_STATUS Status;\r
636 UINT32 ResultLength;\r
637 HTTP_URL_PARSER *Parser;\r
638\r
639 if ((Url == NULL) || (UrlParser == NULL) || (Ip6Address == NULL)) {\r
f9a14916
JW
640 return EFI_INVALID_PARAMETER;\r
641 }\r
642\r
d1050b9d 643 Parser = (HTTP_URL_PARSER *)UrlParser;\r
f9a14916
JW
644\r
645 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {\r
4a6f440f 646 return EFI_NOT_FOUND;\r
f9a14916
JW
647 }\r
648\r
649 //\r
650 // IP-literal = "[" ( IPv6address / IPvFuture ) "]"\r
651 //\r
652 Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;\r
653 if (Length < 2) {\r
654 return EFI_INVALID_PARAMETER;\r
655 }\r
656\r
d1050b9d 657 Ptr = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;\r
f9a14916
JW
658 if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {\r
659 return EFI_INVALID_PARAMETER;\r
660 }\r
661\r
662 Ip6String = AllocatePool (Length);\r
663 if (Ip6String == NULL) {\r
664 return EFI_OUT_OF_RESOURCES;\r
665 }\r
d1102dba 666\r
f9a14916
JW
667 Status = UriPercentDecode (\r
668 Ptr + 1,\r
669 Length - 2,\r
670 Ip6String,\r
671 &ResultLength\r
672 );\r
673 if (EFI_ERROR (Status)) {\r
df642d70 674 FreePool (Ip6String);\r
f9a14916
JW
675 return Status;\r
676 }\r
d1102dba 677\r
f9a14916 678 Ip6String[ResultLength] = '\0';\r
d1050b9d 679 Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);\r
f9a14916
JW
680 FreePool (Ip6String);\r
681\r
682 return Status;\r
683}\r
684\r
685/**\r
686 Get the port number from a HTTP URL.\r
687\r
688 This function will return the port number according to the Url and previous parse result.\r
689\r
690 @param[in] Url The pointer to a HTTP URL string.\r
691 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
692 @param[out] Port Pointer to a buffer to store the port number.\r
693\r
694 @retval EFI_SUCCESS Successfully get the required component.\r
695 @retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid.\r
696 @retval EFI_NOT_FOUND No port number in the URL.\r
697 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
d1102dba 698\r
f9a14916
JW
699**/\r
700EFI_STATUS\r
701EFIAPI\r
702HttpUrlGetPort (\r
d1050b9d
MK
703 IN CHAR8 *Url,\r
704 IN VOID *UrlParser,\r
705 OUT UINT16 *Port\r
f9a14916
JW
706 )\r
707{\r
d1050b9d
MK
708 CHAR8 *PortString;\r
709 EFI_STATUS Status;\r
710 UINTN Index;\r
711 UINTN Data;\r
712 UINT32 ResultLength;\r
713 HTTP_URL_PARSER *Parser;\r
714\r
715 if ((Url == NULL) || (UrlParser == NULL) || (Port == NULL)) {\r
f9a14916
JW
716 return EFI_INVALID_PARAMETER;\r
717 }\r
718\r
d3017dd9
JW
719 *Port = 0;\r
720 Index = 0;\r
721\r
d1050b9d 722 Parser = (HTTP_URL_PARSER *)UrlParser;\r
f9a14916
JW
723\r
724 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {\r
4a6f440f 725 return EFI_NOT_FOUND;\r
f9a14916
JW
726 }\r
727\r
728 PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);\r
729 if (PortString == NULL) {\r
730 return EFI_OUT_OF_RESOURCES;\r
731 }\r
732\r
733 Status = UriPercentDecode (\r
734 Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,\r
735 Parser->FieldData[HTTP_URI_FIELD_PORT].Length,\r
736 PortString,\r
737 &ResultLength\r
738 );\r
739 if (EFI_ERROR (Status)) {\r
df642d70 740 goto ON_EXIT;\r
f9a14916
JW
741 }\r
742\r
743 PortString[ResultLength] = '\0';\r
f9a14916 744\r
d3017dd9
JW
745 while (Index < ResultLength) {\r
746 if (!NET_IS_DIGIT (PortString[Index])) {\r
df642d70
PA
747 Status = EFI_INVALID_PARAMETER;\r
748 goto ON_EXIT;\r
d3017dd9 749 }\r
d1050b9d
MK
750\r
751 Index++;\r
d3017dd9
JW
752 }\r
753\r
d1050b9d 754 Status = AsciiStrDecimalToUintnS (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset, (CHAR8 **)NULL, &Data);\r
962e62bc 755\r
e2d662c3 756 if (Data > HTTP_URI_PORT_MAX_NUM) {\r
df642d70
PA
757 Status = EFI_INVALID_PARAMETER;\r
758 goto ON_EXIT;\r
d3017dd9
JW
759 }\r
760\r
d1050b9d 761 *Port = (UINT16)Data;\r
df642d70
PA
762\r
763ON_EXIT:\r
764 FreePool (PortString);\r
962e62bc 765 return Status;\r
f9a14916
JW
766}\r
767\r
6796629d
FS
768/**\r
769 Get the Path from a HTTP URL.\r
770\r
771 This function will return the Path according to the Url and previous parse result,and\r
772 it is the caller's responsibility to free the buffer returned in *Path.\r
773\r
774 @param[in] Url The pointer to a HTTP URL string.\r
775 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().\r
776 @param[out] Path Pointer to a buffer to store the Path.\r
777\r
778 @retval EFI_SUCCESS Successfully get the required component.\r
779 @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.\r
780 @retval EFI_NOT_FOUND No hostName component in the URL.\r
781 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
d1102dba 782\r
6796629d
FS
783**/\r
784EFI_STATUS\r
785EFIAPI\r
786HttpUrlGetPath (\r
d1050b9d
MK
787 IN CHAR8 *Url,\r
788 IN VOID *UrlParser,\r
789 OUT CHAR8 **Path\r
6796629d
FS
790 )\r
791{\r
d1050b9d
MK
792 CHAR8 *PathStr;\r
793 EFI_STATUS Status;\r
794 UINT32 ResultLength;\r
795 HTTP_URL_PARSER *Parser;\r
6796629d 796\r
d1050b9d 797 if ((Url == NULL) || (UrlParser == NULL) || (Path == NULL)) {\r
6796629d
FS
798 return EFI_INVALID_PARAMETER;\r
799 }\r
800\r
d1050b9d 801 Parser = (HTTP_URL_PARSER *)UrlParser;\r
6796629d
FS
802\r
803 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PATH)) == 0) {\r
804 return EFI_NOT_FOUND;\r
805 }\r
806\r
807 PathStr = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PATH].Length + 1);\r
808 if (PathStr == NULL) {\r
809 return EFI_OUT_OF_RESOURCES;\r
810 }\r
d1102dba 811\r
6796629d
FS
812 Status = UriPercentDecode (\r
813 Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset,\r
814 Parser->FieldData[HTTP_URI_FIELD_PATH].Length,\r
815 PathStr,\r
816 &ResultLength\r
817 );\r
818 if (EFI_ERROR (Status)) {\r
df642d70 819 FreePool (PathStr);\r
6796629d
FS
820 return Status;\r
821 }\r
822\r
823 PathStr[ResultLength] = '\0';\r
d1050b9d 824 *Path = PathStr;\r
6796629d
FS
825 return EFI_SUCCESS;\r
826}\r
827\r
f9a14916
JW
828/**\r
829 Release the resource of the URL parser.\r
830\r
831 @param[in] UrlParser Pointer to the parser.\r
d1102dba 832\r
f9a14916
JW
833**/\r
834VOID\r
835EFIAPI\r
836HttpUrlFreeParser (\r
d1050b9d 837 IN VOID *UrlParser\r
f9a14916
JW
838 )\r
839{\r
840 FreePool (UrlParser);\r
841}\r
842\r
843/**\r
844 Find a specified header field according to the field name.\r
845\r
d1102dba 846 @param[in] HeaderCount Number of HTTP header structures in Headers list.\r
f9a14916 847 @param[in] Headers Array containing list of HTTP headers.\r
d1102dba 848 @param[in] FieldName Null terminated string which describes a field name.\r
f9a14916
JW
849\r
850 @return Pointer to the found header or NULL.\r
851\r
852**/\r
853EFI_HTTP_HEADER *\r
558b99a6
GB
854EFIAPI\r
855HttpFindHeader (\r
d1050b9d
MK
856 IN UINTN HeaderCount,\r
857 IN EFI_HTTP_HEADER *Headers,\r
858 IN CHAR8 *FieldName\r
f9a14916
JW
859 )\r
860{\r
d1050b9d 861 UINTN Index;\r
558b99a6 862\r
d1050b9d 863 if ((HeaderCount == 0) || (Headers == NULL) || (FieldName == NULL)) {\r
f9a14916
JW
864 return NULL;\r
865 }\r
866\r
d1050b9d 867 for (Index = 0; Index < HeaderCount; Index++) {\r
f9a14916
JW
868 //\r
869 // Field names are case-insensitive (RFC 2616).\r
870 //\r
871 if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {\r
872 return &Headers[Index];\r
873 }\r
874 }\r
d1050b9d 875\r
f9a14916
JW
876 return NULL;\r
877}\r
878\r
879typedef enum {\r
880 BodyParserBodyStart,\r
881 BodyParserBodyIdentity,\r
882 BodyParserChunkSizeStart,\r
883 BodyParserChunkSize,\r
884 BodyParserChunkSizeEndCR,\r
885 BodyParserChunkExtStart,\r
886 BodyParserChunkDataStart,\r
887 BodyParserChunkDataEnd,\r
888 BodyParserChunkDataEndCR,\r
889 BodyParserTrailer,\r
890 BodyParserLastCRLF,\r
891 BodyParserLastCRLFEnd,\r
892 BodyParserComplete,\r
893 BodyParserStateMax\r
894} HTTP_BODY_PARSE_STATE;\r
895\r
896typedef struct {\r
d1050b9d
MK
897 BOOLEAN IgnoreBody; // "MUST NOT" include a message-body\r
898 BOOLEAN IsChunked; // "chunked" transfer-coding.\r
899 BOOLEAN ContentLengthIsValid;\r
900 UINTN ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE\r
901\r
902 HTTP_BODY_PARSER_CALLBACK Callback;\r
903 VOID *Context;\r
904 UINTN ParsedBodyLength;\r
905 HTTP_BODY_PARSE_STATE State;\r
906 UINTN CurrentChunkSize;\r
907 UINTN CurrentChunkParsedSize;\r
f9a14916
JW
908} HTTP_BODY_PARSER;\r
909\r
f9a14916
JW
910/**\r
911 Convert an hexadecimal char to a value of type UINTN.\r
912\r
913 @param[in] Char Ascii character.\r
914\r
915 @return Value translated from Char.\r
916\r
917**/\r
918UINTN\r
919HttpIoHexCharToUintn (\r
d1050b9d 920 IN CHAR8 Char\r
f9a14916
JW
921 )\r
922{\r
d1050b9d 923 if ((Char >= '0') && (Char <= '9')) {\r
f9a14916
JW
924 return Char - '0';\r
925 }\r
926\r
62cdd1f5 927 return (10 + AsciiCharToUpper (Char) - 'A');\r
f9a14916
JW
928}\r
929\r
930/**\r
931 Get the value of the content length if there is a "Content-Length" header.\r
932\r
933 @param[in] HeaderCount Number of HTTP header structures in Headers.\r
934 @param[in] Headers Array containing list of HTTP headers.\r
935 @param[out] ContentLength Pointer to save the value of the content length.\r
936\r
937 @retval EFI_SUCCESS Successfully get the content length.\r
938 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.\r
939\r
940**/\r
941EFI_STATUS\r
942HttpIoParseContentLengthHeader (\r
d1050b9d
MK
943 IN UINTN HeaderCount,\r
944 IN EFI_HTTP_HEADER *Headers,\r
945 OUT UINTN *ContentLength\r
f9a14916
JW
946 )\r
947{\r
d1050b9d 948 EFI_HTTP_HEADER *Header;\r
558b99a6
GB
949\r
950 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);\r
f9a14916
JW
951 if (Header == NULL) {\r
952 return EFI_NOT_FOUND;\r
953 }\r
954\r
d1050b9d 955 return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **)NULL, ContentLength);\r
f9a14916
JW
956}\r
957\r
958/**\r
959\r
960 Check whether the HTTP message is using the "chunked" transfer-coding.\r
961\r
962 @param[in] HeaderCount Number of HTTP header structures in Headers.\r
963 @param[in] Headers Array containing list of HTTP headers.\r
964\r
965 @return The message is "chunked" transfer-coding (TRUE) or not (FALSE).\r
d1102dba 966\r
f9a14916
JW
967**/\r
968BOOLEAN\r
969HttpIoIsChunked (\r
d1050b9d
MK
970 IN UINTN HeaderCount,\r
971 IN EFI_HTTP_HEADER *Headers\r
f9a14916
JW
972 )\r
973{\r
d1050b9d 974 EFI_HTTP_HEADER *Header;\r
f9a14916 975\r
558b99a6 976 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);\r
f9a14916
JW
977 if (Header == NULL) {\r
978 return FALSE;\r
979 }\r
980\r
981 if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {\r
982 return TRUE;\r
983 }\r
984\r
985 return FALSE;\r
986}\r
987\r
988/**\r
989 Check whether the HTTP message should have a message-body.\r
990\r
991 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.\r
992 @param[in] StatusCode Response status code returned by the remote host.\r
993\r
994 @return The message should have a message-body (FALSE) or not (TRUE).\r
995\r
996**/\r
997BOOLEAN\r
998HttpIoNoMessageBody (\r
d1050b9d
MK
999 IN EFI_HTTP_METHOD Method,\r
1000 IN EFI_HTTP_STATUS_CODE StatusCode\r
f9a14916
JW
1001 )\r
1002{\r
1003 //\r
1004 // RFC 2616:\r
1005 // All responses to the HEAD request method\r
1006 // MUST NOT include a message-body, even though the presence of entity-\r
1007 // header fields might lead one to believe they do. All 1xx\r
1008 // (informational), 204 (no content), and 304 (not modified) responses\r
1009 // MUST NOT include a message-body. All other responses do include a\r
1010 // message-body, although it MAY be of zero length.\r
1011 //\r
1012 if (Method == HttpMethodHead) {\r
1013 return TRUE;\r
1014 }\r
1015\r
1016 if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||\r
1017 (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||\r
1018 (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||\r
1019 (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))\r
1020 {\r
1021 return TRUE;\r
1022 }\r
1023\r
1024 return FALSE;\r
1025}\r
1026\r
1027/**\r
1028 Initialize a HTTP message-body parser.\r
1029\r
1030 This function will create and initialize a HTTP message parser according to caller provided HTTP message\r
1031 header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().\r
1032\r
1033 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.\r
1034 @param[in] StatusCode Response status code returned by the remote host.\r
1035 @param[in] HeaderCount Number of HTTP header structures in Headers.\r
1036 @param[in] Headers Array containing list of HTTP headers.\r
1037 @param[in] Callback Callback function that is invoked when parsing the HTTP message-body,\r
1038 set to NULL to ignore all events.\r
1039 @param[in] Context Pointer to the context that will be passed to Callback.\r
1040 @param[out] MsgParser Pointer to the returned buffer to store the message parser.\r
1041\r
1042 @retval EFI_SUCCESS Successfully initialized the parser.\r
1043 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
1044 @retval EFI_INVALID_PARAMETER MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.\r
1045 @retval Others Failed to initialize the parser.\r
1046\r
1047**/\r
1048EFI_STATUS\r
1049EFIAPI\r
1050HttpInitMsgParser (\r
d1050b9d
MK
1051 IN EFI_HTTP_METHOD Method,\r
1052 IN EFI_HTTP_STATUS_CODE StatusCode,\r
1053 IN UINTN HeaderCount,\r
1054 IN EFI_HTTP_HEADER *Headers,\r
1055 IN HTTP_BODY_PARSER_CALLBACK Callback,\r
1056 IN VOID *Context,\r
1057 OUT VOID **MsgParser\r
f9a14916
JW
1058 )\r
1059{\r
d1050b9d
MK
1060 EFI_STATUS Status;\r
1061 HTTP_BODY_PARSER *Parser;\r
d1102dba 1062\r
d1050b9d 1063 if ((HeaderCount != 0) && (Headers == NULL)) {\r
f9a14916
JW
1064 return EFI_INVALID_PARAMETER;\r
1065 }\r
1066\r
1067 if (MsgParser == NULL) {\r
1068 return EFI_INVALID_PARAMETER;\r
1069 }\r
1070\r
1071 Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));\r
1072 if (Parser == NULL) {\r
1073 return EFI_OUT_OF_RESOURCES;\r
1074 }\r
1075\r
1076 Parser->State = BodyParserBodyStart;\r
d1102dba 1077\r
f9a14916 1078 //\r
8a1c4a8c 1079 // Determine the message length according to RFC 2616.\r
f9a14916
JW
1080 // 1. Check whether the message "MUST NOT" have a message-body.\r
1081 //\r
1082 Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);\r
1083 //\r
1084 // 2. Check whether the message using "chunked" transfer-coding.\r
1085 //\r
d1050b9d 1086 Parser->IsChunked = HttpIoIsChunked (HeaderCount, Headers);\r
f9a14916
JW
1087 //\r
1088 // 3. Check whether the message has a Content-Length header field.\r
1089 //\r
1090 Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);\r
1091 if (!EFI_ERROR (Status)) {\r
1092 Parser->ContentLengthIsValid = TRUE;\r
1093 }\r
d1050b9d 1094\r
f9a14916
JW
1095 //\r
1096 // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".\r
1097 // 5. By server closing the connection\r
1098 //\r
d1102dba 1099\r
f9a14916
JW
1100 //\r
1101 // Set state to skip body parser if the message shouldn't have a message body.\r
1102 //\r
1103 if (Parser->IgnoreBody) {\r
1104 Parser->State = BodyParserComplete;\r
1105 } else {\r
1106 Parser->Callback = Callback;\r
1107 Parser->Context = Context;\r
1108 }\r
1109\r
1110 *MsgParser = Parser;\r
1111 return EFI_SUCCESS;\r
1112}\r
1113\r
1114/**\r
1115 Parse message body.\r
1116\r
1117 Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.\r
1118\r
1119 @param[in, out] MsgParser Pointer to the message parser.\r
1120 @param[in] BodyLength Length in bytes of the Body.\r
1121 @param[in] Body Pointer to the buffer of the message-body to be parsed.\r
1122\r
1123 @retval EFI_SUCCESS Successfully parse the message-body.\r
1124 @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0.\r
4a6f440f
JW
1125 @retval EFI_ABORTED Operation aborted.\r
1126 @retval Other Error happened while parsing message body.\r
f9a14916
JW
1127\r
1128**/\r
1129EFI_STATUS\r
1130EFIAPI\r
1131HttpParseMessageBody (\r
d1050b9d
MK
1132 IN OUT VOID *MsgParser,\r
1133 IN UINTN BodyLength,\r
1134 IN CHAR8 *Body\r
f9a14916
JW
1135 )\r
1136{\r
d1050b9d
MK
1137 CHAR8 *Char;\r
1138 UINTN RemainderLengthInThis;\r
1139 UINTN LengthForCallback;\r
1140 UINTN PortionLength;\r
1141 EFI_STATUS Status;\r
1142 HTTP_BODY_PARSER *Parser;\r
1143\r
1144 if ((BodyLength == 0) || (Body == NULL)) {\r
f9a14916
JW
1145 return EFI_INVALID_PARAMETER;\r
1146 }\r
1147\r
1148 if (MsgParser == NULL) {\r
1149 return EFI_INVALID_PARAMETER;\r
1150 }\r
1151\r
d1050b9d 1152 Parser = (HTTP_BODY_PARSER *)MsgParser;\r
f9a14916
JW
1153\r
1154 if (Parser->IgnoreBody) {\r
1155 Parser->State = BodyParserComplete;\r
1156 if (Parser->Callback != NULL) {\r
1157 Status = Parser->Callback (\r
0d07e6fb
JW
1158 BodyParseEventOnComplete,\r
1159 Body,\r
1160 0,\r
1161 Parser->Context\r
1162 );\r
f9a14916
JW
1163 if (EFI_ERROR (Status)) {\r
1164 return Status;\r
1165 }\r
1166 }\r
d1050b9d 1167\r
f9a14916
JW
1168 return EFI_SUCCESS;\r
1169 }\r
1170\r
1171 if (Parser->State == BodyParserBodyStart) {\r
1172 Parser->ParsedBodyLength = 0;\r
1173 if (Parser->IsChunked) {\r
1174 Parser->State = BodyParserChunkSizeStart;\r
1175 } else {\r
1176 Parser->State = BodyParserBodyIdentity;\r
1177 }\r
1178 }\r
1179\r
1180 //\r
1181 // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.\r
1182 //\r
1183 for (Char = Body; Char < Body + BodyLength; ) {\r
f9a14916 1184 switch (Parser->State) {\r
d1050b9d
MK
1185 case BodyParserStateMax:\r
1186 return EFI_ABORTED;\r
d1102dba 1187\r
d1050b9d 1188 case BodyParserBodyIdentity:\r
70c2f10f 1189 //\r
d1050b9d 1190 // Identity transfer-coding, just notify user to save the body data.\r
70c2f10f 1191 //\r
d1050b9d
MK
1192 PortionLength = MIN (\r
1193 BodyLength,\r
1194 Parser->ContentLength - Parser->ParsedBodyLength\r
1195 );\r
1196 if (PortionLength == 0) {\r
1197 //\r
1198 // Got BodyLength, but no ContentLength. Use BodyLength.\r
1199 //\r
1200 PortionLength = BodyLength;\r
1201 Parser->ContentLength = PortionLength;\r
f9a14916 1202 }\r
d1050b9d 1203\r
47bda627
ZL
1204 if (Parser->Callback != NULL) {\r
1205 Status = Parser->Callback (\r
d1050b9d 1206 BodyParseEventOnData,\r
0d07e6fb 1207 Char,\r
d1050b9d 1208 PortionLength,\r
0d07e6fb
JW
1209 Parser->Context\r
1210 );\r
47bda627
ZL
1211 if (EFI_ERROR (Status)) {\r
1212 return Status;\r
1213 }\r
1214 }\r
f9a14916 1215\r
d1050b9d
MK
1216 Char += PortionLength;\r
1217 Parser->ParsedBodyLength += PortionLength;\r
1218 if (Parser->ParsedBodyLength == Parser->ContentLength) {\r
1219 Parser->State = BodyParserComplete;\r
1220 if (Parser->Callback != NULL) {\r
1221 Status = Parser->Callback (\r
1222 BodyParseEventOnComplete,\r
1223 Char,\r
1224 0,\r
1225 Parser->Context\r
1226 );\r
1227 if (EFI_ERROR (Status)) {\r
1228 return Status;\r
1229 }\r
1230 }\r
f9a14916 1231 }\r
d1050b9d 1232\r
f9a14916 1233 break;\r
f9a14916 1234\r
d1050b9d
MK
1235 case BodyParserChunkSizeStart:\r
1236 //\r
1237 // First byte of chunk-size, the chunk-size might be truncated.\r
1238 //\r
1239 Parser->CurrentChunkSize = 0;\r
1240 Parser->State = BodyParserChunkSize;\r
1241 case BodyParserChunkSize:\r
1242 if (!NET_IS_HEX_CHAR (*Char)) {\r
1243 if (*Char == ';') {\r
1244 Parser->State = BodyParserChunkExtStart;\r
1245 Char++;\r
1246 } else if (*Char == '\r') {\r
1247 Parser->State = BodyParserChunkSizeEndCR;\r
1248 Char++;\r
1249 } else {\r
1250 Parser->State = BodyParserStateMax;\r
1251 }\r
f9a14916 1252\r
d1050b9d
MK
1253 break;\r
1254 }\r
1255\r
1256 if (Parser->CurrentChunkSize > (((~((UINTN)0)) - 16) / 16)) {\r
1257 return EFI_INVALID_PARAMETER;\r
1258 }\r
d1102dba 1259\r
d1050b9d
MK
1260 Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);\r
1261 Char++;\r
f9a14916 1262 break;\r
d1050b9d
MK
1263\r
1264 case BodyParserChunkExtStart:\r
f9a14916 1265 //\r
d1050b9d 1266 // Ignore all the chunk extensions.\r
f9a14916 1267 //\r
d1050b9d
MK
1268 if (*Char == '\r') {\r
1269 Parser->State = BodyParserChunkSizeEndCR;\r
1270 }\r
d1102dba 1271\r
f9a14916 1272 Char++;\r
f9a14916 1273 break;\r
d1050b9d
MK
1274\r
1275 case BodyParserChunkSizeEndCR:\r
1276 if (*Char != '\n') {\r
1277 Parser->State = BodyParserStateMax;\r
1278 break;\r
1279 }\r
1280\r
1281 Char++;\r
1282 if (Parser->CurrentChunkSize == 0) {\r
1283 //\r
1284 // The last chunk has been parsed and now assumed the state\r
1285 // of HttpBodyParse is ParserLastCRLF. So it need to decide\r
1286 // whether the rest message is trailer or last CRLF in the next round.\r
1287 //\r
1288 Parser->ContentLengthIsValid = TRUE;\r
1289 Parser->State = BodyParserLastCRLF;\r
1290 break;\r
1291 }\r
1292\r
1293 Parser->State = BodyParserChunkDataStart;\r
1294 Parser->CurrentChunkParsedSize = 0;\r
f9a14916 1295 break;\r
d1102dba 1296\r
d1050b9d
MK
1297 case BodyParserLastCRLF:\r
1298 //\r
1299 // Judge the byte is belong to the Last CRLF or trailer, and then\r
1300 // configure the state of HttpBodyParse to corresponding state.\r
1301 //\r
1302 if (*Char == '\r') {\r
1303 Char++;\r
1304 Parser->State = BodyParserLastCRLFEnd;\r
1305 break;\r
1306 } else {\r
1307 Parser->State = BodyParserTrailer;\r
1308 break;\r
1309 }\r
1310\r
1311 case BodyParserLastCRLFEnd:\r
1312 if (*Char == '\n') {\r
1313 Parser->State = BodyParserComplete;\r
1314 Char++;\r
1315 if (Parser->Callback != NULL) {\r
1316 Status = Parser->Callback (\r
1317 BodyParseEventOnComplete,\r
1318 Char,\r
1319 0,\r
1320 Parser->Context\r
1321 );\r
1322 if (EFI_ERROR (Status)) {\r
1323 return Status;\r
1324 }\r
1325 }\r
1326\r
1327 break;\r
1328 } else {\r
1329 Parser->State = BodyParserStateMax;\r
1330 break;\r
1331 }\r
1332\r
1333 case BodyParserTrailer:\r
1334 if (*Char == '\r') {\r
1335 Parser->State = BodyParserChunkSizeEndCR;\r
1336 }\r
1337\r
47bda627 1338 Char++;\r
d1050b9d
MK
1339 break;\r
1340\r
1341 case BodyParserChunkDataStart:\r
1342 //\r
1343 // First byte of chunk-data, the chunk data also might be truncated.\r
1344 //\r
1345 RemainderLengthInThis = BodyLength - (Char - Body);\r
1346 LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);\r
47bda627
ZL
1347 if (Parser->Callback != NULL) {\r
1348 Status = Parser->Callback (\r
d1050b9d 1349 BodyParseEventOnData,\r
0d07e6fb 1350 Char,\r
d1050b9d 1351 LengthForCallback,\r
0d07e6fb
JW
1352 Parser->Context\r
1353 );\r
47bda627
ZL
1354 if (EFI_ERROR (Status)) {\r
1355 return Status;\r
1356 }\r
1357 }\r
d1102dba 1358\r
d1050b9d
MK
1359 Char += LengthForCallback;\r
1360 Parser->ContentLength += LengthForCallback;\r
1361 Parser->CurrentChunkParsedSize += LengthForCallback;\r
1362 if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {\r
1363 Parser->State = BodyParserChunkDataEnd;\r
1364 }\r
f9a14916 1365\r
d1050b9d
MK
1366 break;\r
1367\r
1368 case BodyParserChunkDataEnd:\r
1369 if (*Char == '\r') {\r
1370 Parser->State = BodyParserChunkDataEndCR;\r
1371 } else {\r
1372 Parser->State = BodyParserStateMax;\r
f9a14916 1373 }\r
f9a14916 1374\r
d1050b9d
MK
1375 Char++;\r
1376 break;\r
1377\r
1378 case BodyParserChunkDataEndCR:\r
1379 if (*Char != '\n') {\r
1380 Parser->State = BodyParserStateMax;\r
1381 break;\r
1382 }\r
f9a14916 1383\r
d1050b9d
MK
1384 Char++;\r
1385 Parser->State = BodyParserChunkSizeStart;\r
f9a14916 1386 break;\r
f9a14916 1387\r
d1050b9d
MK
1388 default:\r
1389 break;\r
f9a14916 1390 }\r
f9a14916
JW
1391 }\r
1392\r
1393 if (Parser->State == BodyParserStateMax) {\r
1394 return EFI_ABORTED;\r
1395 }\r
1396\r
1397 return EFI_SUCCESS;\r
1398}\r
1399\r
1400/**\r
1401 Check whether the message-body is complete or not.\r
1402\r
1403 @param[in] MsgParser Pointer to the message parser.\r
1404\r
1405 @retval TRUE Message-body is complete.\r
1406 @retval FALSE Message-body is not complete.\r
1407\r
1408**/\r
1409BOOLEAN\r
1410EFIAPI\r
1411HttpIsMessageComplete (\r
d1050b9d 1412 IN VOID *MsgParser\r
f9a14916
JW
1413 )\r
1414{\r
d1050b9d 1415 HTTP_BODY_PARSER *Parser;\r
f9a14916 1416\r
0efeec8e
JW
1417 if (MsgParser == NULL) {\r
1418 return FALSE;\r
1419 }\r
1420\r
d1050b9d 1421 Parser = (HTTP_BODY_PARSER *)MsgParser;\r
f9a14916
JW
1422\r
1423 if (Parser->State == BodyParserComplete) {\r
1424 return TRUE;\r
1425 }\r
d1050b9d 1426\r
f9a14916
JW
1427 return FALSE;\r
1428}\r
1429\r
1430/**\r
1431 Get the content length of the entity.\r
1432\r
1433 Note that in trunk transfer, the entity length is not valid until the whole message body is received.\r
1434\r
1435 @param[in] MsgParser Pointer to the message parser.\r
1436 @param[out] ContentLength Pointer to store the length of the entity.\r
1437\r
1438 @retval EFI_SUCCESS Successfully to get the entity length.\r
1439 @retval EFI_NOT_READY Entity length is not valid yet.\r
1440 @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.\r
d1102dba 1441\r
f9a14916
JW
1442**/\r
1443EFI_STATUS\r
1444EFIAPI\r
1445HttpGetEntityLength (\r
d1050b9d
MK
1446 IN VOID *MsgParser,\r
1447 OUT UINTN *ContentLength\r
f9a14916
JW
1448 )\r
1449{\r
d1050b9d 1450 HTTP_BODY_PARSER *Parser;\r
f9a14916 1451\r
d1050b9d 1452 if ((MsgParser == NULL) || (ContentLength == NULL)) {\r
f9a14916
JW
1453 return EFI_INVALID_PARAMETER;\r
1454 }\r
1455\r
d1050b9d 1456 Parser = (HTTP_BODY_PARSER *)MsgParser;\r
f9a14916
JW
1457\r
1458 if (!Parser->ContentLengthIsValid) {\r
1459 return EFI_NOT_READY;\r
1460 }\r
1461\r
1462 *ContentLength = Parser->ContentLength;\r
1463 return EFI_SUCCESS;\r
1464}\r
1465\r
1466/**\r
1467 Release the resource of the message parser.\r
1468\r
1469 @param[in] MsgParser Pointer to the message parser.\r
d1102dba 1470\r
f9a14916
JW
1471**/\r
1472VOID\r
1473EFIAPI\r
1474HttpFreeMsgParser (\r
d1050b9d 1475 IN VOID *MsgParser\r
f9a14916
JW
1476 )\r
1477{\r
1478 FreePool (MsgParser);\r
1479}\r
558b99a6 1480\r
558b99a6 1481/**\r
3b28e744 1482 Get the next string, which is distinguished by specified separator.\r
558b99a6
GB
1483\r
1484 @param[in] String Pointer to the string.\r
3b28e744 1485 @param[in] Separator Specified separator used to distinguish where is the beginning\r
558b99a6
GB
1486 of next string.\r
1487\r
1488 @return Pointer to the next string.\r
1489 @return NULL if not find or String is NULL.\r
1490\r
1491**/\r
1492CHAR8 *\r
558b99a6 1493AsciiStrGetNextToken (\r
d1050b9d
MK
1494 IN CONST CHAR8 *String,\r
1495 IN CHAR8 Separator\r
558b99a6
GB
1496 )\r
1497{\r
d1050b9d 1498 CONST CHAR8 *Token;\r
558b99a6
GB
1499\r
1500 Token = String;\r
1501 while (TRUE) {\r
1502 if (*Token == 0) {\r
1503 return NULL;\r
1504 }\r
d1050b9d 1505\r
3b28e744 1506 if (*Token == Separator) {\r
558b99a6
GB
1507 return (CHAR8 *)(Token + 1);\r
1508 }\r
d1050b9d 1509\r
558b99a6
GB
1510 Token++;\r
1511 }\r
1512}\r
1513\r
1514/**\r
1515 Set FieldName and FieldValue into specified HttpHeader.\r
1516\r
1517 @param[in,out] HttpHeader Specified HttpHeader.\r
1518 @param[in] FieldName FieldName of this HttpHeader, a NULL terminated ASCII string.\r
1519 @param[in] FieldValue FieldValue of this HttpHeader, a NULL terminated ASCII string.\r
1520\r
1521\r
1522 @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully.\r
0efeec8e 1523 @retval EFI_INVALID_PARAMETER The parameter is invalid.\r
558b99a6
GB
1524 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
1525\r
1526**/\r
1527EFI_STATUS\r
1528EFIAPI\r
1529HttpSetFieldNameAndValue (\r
d1050b9d
MK
1530 IN OUT EFI_HTTP_HEADER *HttpHeader,\r
1531 IN CONST CHAR8 *FieldName,\r
1532 IN CONST CHAR8 *FieldValue\r
558b99a6
GB
1533 )\r
1534{\r
d1050b9d
MK
1535 UINTN FieldNameSize;\r
1536 UINTN FieldValueSize;\r
558b99a6 1537\r
d1050b9d 1538 if ((HttpHeader == NULL) || (FieldName == NULL) || (FieldValue == NULL)) {\r
0efeec8e
JW
1539 return EFI_INVALID_PARAMETER;\r
1540 }\r
1541\r
558b99a6
GB
1542 if (HttpHeader->FieldName != NULL) {\r
1543 FreePool (HttpHeader->FieldName);\r
1544 }\r
d1050b9d 1545\r
558b99a6
GB
1546 if (HttpHeader->FieldValue != NULL) {\r
1547 FreePool (HttpHeader->FieldValue);\r
1548 }\r
1549\r
d1050b9d 1550 FieldNameSize = AsciiStrSize (FieldName);\r
558b99a6
GB
1551 HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);\r
1552 if (HttpHeader->FieldName == NULL) {\r
1553 return EFI_OUT_OF_RESOURCES;\r
1554 }\r
d1050b9d 1555\r
558b99a6
GB
1556 CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);\r
1557 HttpHeader->FieldName[FieldNameSize - 1] = 0;\r
1558\r
d1050b9d 1559 FieldValueSize = AsciiStrSize (FieldValue);\r
558b99a6
GB
1560 HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);\r
1561 if (HttpHeader->FieldValue == NULL) {\r
f5168b84 1562 FreePool (HttpHeader->FieldName);\r
558b99a6
GB
1563 return EFI_OUT_OF_RESOURCES;\r
1564 }\r
d1050b9d 1565\r
558b99a6
GB
1566 CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);\r
1567 HttpHeader->FieldValue[FieldValueSize - 1] = 0;\r
1568\r
1569 return EFI_SUCCESS;\r
1570}\r
1571\r
1572/**\r
1573 Get one key/value header pair from the raw string.\r
1574\r
1575 @param[in] String Pointer to the raw string.\r
1576 @param[out] FieldName Points directly to field name within 'HttpHeader'.\r
1577 @param[out] FieldValue Points directly to field value within 'HttpHeader'.\r
1578\r
1579 @return Pointer to the next raw string.\r
1580 @return NULL if no key/value header pair from this raw string.\r
1581\r
1582**/\r
1583CHAR8 *\r
1584EFIAPI\r
1585HttpGetFieldNameAndValue (\r
d1050b9d
MK
1586 IN CHAR8 *String,\r
1587 OUT CHAR8 **FieldName,\r
1588 OUT CHAR8 **FieldValue\r
558b99a6
GB
1589 )\r
1590{\r
1591 CHAR8 *FieldNameStr;\r
1592 CHAR8 *FieldValueStr;\r
1593 CHAR8 *StrPtr;\r
b2a73b5b 1594 CHAR8 *EndofHeader;\r
558b99a6 1595\r
d1050b9d 1596 if ((String == NULL) || (FieldName == NULL) || (FieldValue == NULL)) {\r
558b99a6
GB
1597 return NULL;\r
1598 }\r
1599\r
1600 *FieldName = NULL;\r
1601 *FieldValue = NULL;\r
1602 FieldNameStr = NULL;\r
1603 FieldValueStr = NULL;\r
1604 StrPtr = NULL;\r
b2a73b5b
JW
1605 EndofHeader = NULL;\r
1606\r
b2a73b5b
JW
1607 //\r
1608 // Check whether the raw HTTP header string is valid or not.\r
1609 //\r
1610 EndofHeader = AsciiStrStr (String, "\r\n\r\n");\r
1611 if (EndofHeader == NULL) {\r
1612 return NULL;\r
1613 }\r
558b99a6
GB
1614\r
1615 //\r
1616 // Each header field consists of a name followed by a colon (":") and the field value.\r
e9d21340
JW
1617 // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.\r
1618 //\r
1619 // message-header = field-name ":" [ field-value ]\r
1620 // field-name = token\r
1621 // field-value = *( field-content | LWS )\r
1622 //\r
1623 // Note: "*(element)" allows any number element, including zero; "1*(element)" requires at least one element.\r
1624 // [element] means element is optional.\r
1625 // LWS = [CRLF] 1*(SP|HT), it can be ' ' or '\t' or '\r\n ' or '\r\n\t'.\r
1626 // CRLF = '\r\n'.\r
1627 // SP = ' '.\r
1628 // HT = '\t' (Tab).\r
558b99a6 1629 //\r
d1050b9d 1630 FieldNameStr = String;\r
558b99a6
GB
1631 FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');\r
1632 if (FieldValueStr == NULL) {\r
1633 return NULL;\r
1634 }\r
1635\r
1636 //\r
e9d21340 1637 // Replace ':' with 0, then FieldName has been retrived from String.\r
558b99a6
GB
1638 //\r
1639 *(FieldValueStr - 1) = 0;\r
1640\r
1641 //\r
e9d21340 1642 // Handle FieldValueStr, skip all the preceded LWS.\r
558b99a6
GB
1643 //\r
1644 while (TRUE) {\r
d1050b9d 1645 if ((*FieldValueStr == ' ') || (*FieldValueStr == '\t')) {\r
b2a73b5b 1646 //\r
d1102dba 1647 // Boundary condition check.\r
b2a73b5b 1648 //\r
d1050b9d 1649 if ((UINTN)EndofHeader - (UINTN)FieldValueStr < 1) {\r
e9d21340
JW
1650 //\r
1651 // Wrong String format!\r
1652 //\r
d1102dba 1653 return NULL;\r
b2a73b5b 1654 }\r
d1102dba 1655\r
d1050b9d 1656 FieldValueStr++;\r
b2a73b5b
JW
1657 } else if (*FieldValueStr == '\r') {\r
1658 //\r
d1102dba 1659 // Boundary condition check.\r
b2a73b5b 1660 //\r
d1050b9d 1661 if ((UINTN)EndofHeader - (UINTN)FieldValueStr < 3) {\r
e9d21340
JW
1662 //\r
1663 // No more preceded LWS, so break here.\r
1664 //\r
1665 break;\r
b2a73b5b
JW
1666 }\r
1667\r
e9d21340 1668 if (*(FieldValueStr + 1) == '\n' ) {\r
d1050b9d 1669 if ((*(FieldValueStr + 2) == ' ') || (*(FieldValueStr + 2) == '\t')) {\r
e9d21340
JW
1670 FieldValueStr = FieldValueStr + 3;\r
1671 } else {\r
1672 //\r
1673 // No more preceded LWS, so break here.\r
1674 //\r
1675 break;\r
1676 }\r
1677 } else {\r
1678 //\r
1679 // Wrong String format!\r
1680 //\r
1681 return NULL;\r
b2a73b5b 1682 }\r
558b99a6 1683 } else {\r
e9d21340
JW
1684 //\r
1685 // No more preceded LWS, so break here.\r
1686 //\r
558b99a6
GB
1687 break;\r
1688 }\r
1689 }\r
1690\r
558b99a6
GB
1691 StrPtr = FieldValueStr;\r
1692 do {\r
e9d21340
JW
1693 //\r
1694 // Handle the LWS within the field value.\r
1695 //\r
558b99a6 1696 StrPtr = AsciiStrGetNextToken (StrPtr, '\r');\r
d1050b9d 1697 if ((StrPtr == NULL) || (*StrPtr != '\n')) {\r
e9d21340
JW
1698 //\r
1699 // Wrong String format!\r
1700 //\r
558b99a6
GB
1701 return NULL;\r
1702 }\r
1703\r
1704 StrPtr++;\r
1705 } while (*StrPtr == ' ' || *StrPtr == '\t');\r
1706\r
1707 //\r
1708 // Replace '\r' with 0\r
1709 //\r
1710 *(StrPtr - 2) = 0;\r
1711\r
1712 //\r
1713 // Get FieldName and FieldValue.\r
1714 //\r
d1050b9d 1715 *FieldName = FieldNameStr;\r
558b99a6
GB
1716 *FieldValue = FieldValueStr;\r
1717\r
1718 return StrPtr;\r
1719}\r
1720\r
1721/**\r
1722 Free existing HeaderFields.\r
1723\r
6deb4baa 1724 @param[in] HeaderFields Pointer to array of key/value header pairs waiting for free.\r
558b99a6
GB
1725 @param[in] FieldCount The number of header pairs in HeaderFields.\r
1726\r
1727**/\r
1728VOID\r
1729EFIAPI\r
1730HttpFreeHeaderFields (\r
1731 IN EFI_HTTP_HEADER *HeaderFields,\r
1732 IN UINTN FieldCount\r
1733 )\r
1734{\r
d1050b9d 1735 UINTN Index;\r
558b99a6
GB
1736\r
1737 if (HeaderFields != NULL) {\r
1738 for (Index = 0; Index < FieldCount; Index++) {\r
1739 if (HeaderFields[Index].FieldName != NULL) {\r
1740 FreePool (HeaderFields[Index].FieldName);\r
1741 }\r
d1050b9d 1742\r
558b99a6
GB
1743 if (HeaderFields[Index].FieldValue != NULL) {\r
1744 FreePool (HeaderFields[Index].FieldValue);\r
1745 }\r
1746 }\r
1747\r
1748 FreePool (HeaderFields);\r
1749 }\r
1750}\r
1751\r
1752/**\r
e297a0a4 1753 Generate HTTP request message.\r
558b99a6 1754\r
e297a0a4
NH
1755 This function will allocate memory for the whole HTTP message and generate a\r
1756 well formatted HTTP Request message in it, include the Request-Line, header\r
1757 fields and also the message body. It is the caller's responsibility to free\r
1758 the buffer returned in *RequestMsg.\r
1759\r
1760 @param[in] Message Pointer to the EFI_HTTP_MESSAGE structure which\r
1761 contains the required information to generate\r
1762 the HTTP request message.\r
558b99a6 1763 @param[in] Url The URL of a remote host.\r
e297a0a4 1764 @param[out] RequestMsg Pointer to the created HTTP request message.\r
6deb4baa 1765 NULL if any error occurred.\r
e297a0a4 1766 @param[out] RequestMsgSize Size of the RequestMsg (in bytes).\r
558b99a6 1767\r
0d07e6fb 1768 @retval EFI_SUCCESS If HTTP request string was created successfully.\r
558b99a6 1769 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
0d07e6fb 1770 @retval EFI_INVALID_PARAMETER The input arguments are invalid.\r
558b99a6
GB
1771\r
1772**/\r
1773EFI_STATUS\r
1774EFIAPI\r
e297a0a4 1775HttpGenRequestMessage (\r
d1050b9d
MK
1776 IN CONST EFI_HTTP_MESSAGE *Message,\r
1777 IN CONST CHAR8 *Url,\r
1778 OUT CHAR8 **RequestMsg,\r
1779 OUT UINTN *RequestMsgSize\r
558b99a6
GB
1780 )\r
1781{\r
d1050b9d
MK
1782 EFI_STATUS Status;\r
1783 UINTN StrLength;\r
1784 CHAR8 *RequestPtr;\r
1785 UINTN HttpHdrSize;\r
1786 UINTN MsgSize;\r
1787 BOOLEAN Success;\r
1788 VOID *HttpHdr;\r
1789 EFI_HTTP_HEADER **AppendList;\r
1790 UINTN Index;\r
1791 EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilitiesProtocol;\r
558b99a6 1792\r
1fed57ab
JW
1793 Status = EFI_SUCCESS;\r
1794 HttpHdrSize = 0;\r
1795 MsgSize = 0;\r
1796 Success = FALSE;\r
1797 HttpHdr = NULL;\r
1798 AppendList = NULL;\r
558b99a6
GB
1799 HttpUtilitiesProtocol = NULL;\r
1800\r
1801 //\r
09991304
NH
1802 // 1. If we have a Request, we cannot have a NULL Url\r
1803 // 2. If we have a Request, HeaderCount can not be non-zero\r
1804 // 3. If we do not have a Request, HeaderCount should be zero\r
1805 // 4. If we do not have Request and Headers, we need at least a message-body\r
558b99a6 1806 //\r
d1050b9d
MK
1807 if (((Message == NULL) || (RequestMsg == NULL) || (RequestMsgSize == NULL)) ||\r
1808 ((Message->Data.Request != NULL) && (Url == NULL)) ||\r
1809 ((Message->Data.Request != NULL) && (Message->HeaderCount == 0)) ||\r
1810 ((Message->Data.Request == NULL) && (Message->HeaderCount != 0)) ||\r
1811 ((Message->Data.Request == NULL) && (Message->HeaderCount == 0) && (Message->BodyLength == 0)))\r
1812 {\r
09991304
NH
1813 return EFI_INVALID_PARAMETER;\r
1814 }\r
558b99a6 1815\r
09991304
NH
1816 if (Message->HeaderCount != 0) {\r
1817 //\r
1818 // Locate the HTTP_UTILITIES protocol.\r
1819 //\r
1820 Status = gBS->LocateProtocol (\r
1821 &gEfiHttpUtilitiesProtocolGuid,\r
1822 NULL,\r
d1050b9d 1823 (VOID **)&HttpUtilitiesProtocol\r
09991304
NH
1824 );\r
1825\r
1826 if (EFI_ERROR (Status)) {\r
d1050b9d 1827 DEBUG ((DEBUG_ERROR, "Failed to locate Http Utilities protocol. Status = %r.\n", Status));\r
09991304
NH
1828 return Status;\r
1829 }\r
1830\r
1831 //\r
1832 // Build AppendList to send into HttpUtilitiesBuild\r
1833 //\r
1834 AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));\r
1835 if (AppendList == NULL) {\r
1836 return EFI_OUT_OF_RESOURCES;\r
1837 }\r
1838\r
d1050b9d 1839 for (Index = 0; Index < Message->HeaderCount; Index++) {\r
09991304
NH
1840 AppendList[Index] = &Message->Headers[Index];\r
1841 }\r
1842\r
1843 //\r
1844 // Build raw HTTP Headers\r
1845 //\r
1846 Status = HttpUtilitiesProtocol->Build (\r
0d07e6fb
JW
1847 HttpUtilitiesProtocol,\r
1848 0,\r
1849 NULL,\r
1850 0,\r
1851 NULL,\r
1852 Message->HeaderCount,\r
1853 AppendList,\r
1854 &HttpHdrSize,\r
1855 &HttpHdr\r
1856 );\r
1857\r
1858 FreePool (AppendList);\r
09991304 1859\r
d1050b9d 1860 if (EFI_ERROR (Status) || (HttpHdr == NULL)) {\r
09991304
NH
1861 return Status;\r
1862 }\r
558b99a6
GB
1863 }\r
1864\r
1865 //\r
09991304 1866 // If we have headers to be sent, account for it.\r
558b99a6 1867 //\r
09991304
NH
1868 if (Message->HeaderCount != 0) {\r
1869 MsgSize = HttpHdrSize;\r
558b99a6
GB
1870 }\r
1871\r
1872 //\r
09991304 1873 // If we have a request line, account for the fields.\r
558b99a6 1874 //\r
09991304
NH
1875 if (Message->Data.Request != NULL) {\r
1876 MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url);\r
558b99a6
GB
1877 }\r
1878\r
1879 //\r
09991304 1880 // If we have a message body to be sent, account for it.\r
558b99a6 1881 //\r
09991304 1882 MsgSize += Message->BodyLength;\r
558b99a6 1883\r
09991304
NH
1884 //\r
1885 // memory for the string that needs to be sent to TCP\r
1886 //\r
0d07e6fb 1887 *RequestMsg = NULL;\r
e297a0a4
NH
1888 *RequestMsg = AllocateZeroPool (MsgSize);\r
1889 if (*RequestMsg == NULL) {\r
558b99a6
GB
1890 Status = EFI_OUT_OF_RESOURCES;\r
1891 goto Exit;\r
1892 }\r
1893\r
e297a0a4 1894 RequestPtr = *RequestMsg;\r
558b99a6
GB
1895 //\r
1896 // Construct header request\r
1897 //\r
09991304
NH
1898 if (Message->Data.Request != NULL) {\r
1899 switch (Message->Data.Request->Method) {\r
d1050b9d
MK
1900 case HttpMethodGet:\r
1901 StrLength = sizeof (HTTP_METHOD_GET) - 1;\r
1902 CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength);\r
1903 RequestPtr += StrLength;\r
1904 break;\r
1905 case HttpMethodPut:\r
1906 StrLength = sizeof (HTTP_METHOD_PUT) - 1;\r
1907 CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength);\r
1908 RequestPtr += StrLength;\r
1909 break;\r
1910 case HttpMethodPatch:\r
1911 StrLength = sizeof (HTTP_METHOD_PATCH) - 1;\r
1912 CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength);\r
1913 RequestPtr += StrLength;\r
1914 break;\r
1915 case HttpMethodPost:\r
1916 StrLength = sizeof (HTTP_METHOD_POST) - 1;\r
1917 CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength);\r
1918 RequestPtr += StrLength;\r
1919 break;\r
1920 case HttpMethodHead:\r
1921 StrLength = sizeof (HTTP_METHOD_HEAD) - 1;\r
1922 CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength);\r
1923 RequestPtr += StrLength;\r
1924 break;\r
1925 case HttpMethodDelete:\r
1926 StrLength = sizeof (HTTP_METHOD_DELETE) - 1;\r
1927 CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);\r
1928 RequestPtr += StrLength;\r
1929 break;\r
1930 default:\r
1931 ASSERT (FALSE);\r
1932 Status = EFI_INVALID_PARAMETER;\r
1933 goto Exit;\r
09991304 1934 }\r
558b99a6 1935\r
d1050b9d 1936 StrLength = AsciiStrLen (EMPTY_SPACE);\r
09991304
NH
1937 CopyMem (RequestPtr, EMPTY_SPACE, StrLength);\r
1938 RequestPtr += StrLength;\r
558b99a6 1939\r
09991304
NH
1940 StrLength = AsciiStrLen (Url);\r
1941 CopyMem (RequestPtr, Url, StrLength);\r
1942 RequestPtr += StrLength;\r
558b99a6 1943\r
09991304
NH
1944 StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;\r
1945 CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);\r
1946 RequestPtr += StrLength;\r
558b99a6 1947\r
09991304
NH
1948 if (HttpHdr != NULL) {\r
1949 //\r
1950 // Construct header\r
1951 //\r
1952 CopyMem (RequestPtr, HttpHdr, HttpHdrSize);\r
1953 RequestPtr += HttpHdrSize;\r
1954 }\r
1955 }\r
558b99a6 1956\r
e297a0a4
NH
1957 //\r
1958 // Construct body\r
1959 //\r
1960 if (Message->Body != NULL) {\r
1961 CopyMem (RequestPtr, Message->Body, Message->BodyLength);\r
1962 RequestPtr += Message->BodyLength;\r
1963 }\r
1964\r
558b99a6
GB
1965 //\r
1966 // Done\r
1967 //\r
e297a0a4 1968 (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg);\r
d1050b9d 1969 Success = TRUE;\r
558b99a6
GB
1970\r
1971Exit:\r
1972\r
1973 if (!Success) {\r
e297a0a4
NH
1974 if (*RequestMsg != NULL) {\r
1975 FreePool (*RequestMsg);\r
558b99a6 1976 }\r
d1050b9d 1977\r
e297a0a4 1978 *RequestMsg = NULL;\r
558b99a6
GB
1979 return Status;\r
1980 }\r
1981\r
1982 if (HttpHdr != NULL) {\r
1983 FreePool (HttpHdr);\r
1984 }\r
1985\r
1986 return EFI_SUCCESS;\r
1987}\r
1988\r
1989/**\r
1990 Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined\r
1991 in UEFI 2.5 specification.\r
1992\r
1993 @param[in] StatusCode The status code value in HTTP message.\r
1994\r
1995 @return Value defined in EFI_HTTP_STATUS_CODE .\r
1996\r
1997**/\r
1998EFI_HTTP_STATUS_CODE\r
1999EFIAPI\r
2000HttpMappingToStatusCode (\r
d1050b9d 2001 IN UINTN StatusCode\r
558b99a6
GB
2002 )\r
2003{\r
2004 switch (StatusCode) {\r
d1050b9d
MK
2005 case 100:\r
2006 return HTTP_STATUS_100_CONTINUE;\r
2007 case 101:\r
2008 return HTTP_STATUS_101_SWITCHING_PROTOCOLS;\r
2009 case 200:\r
2010 return HTTP_STATUS_200_OK;\r
2011 case 201:\r
2012 return HTTP_STATUS_201_CREATED;\r
2013 case 202:\r
2014 return HTTP_STATUS_202_ACCEPTED;\r
2015 case 203:\r
2016 return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;\r
2017 case 204:\r
2018 return HTTP_STATUS_204_NO_CONTENT;\r
2019 case 205:\r
2020 return HTTP_STATUS_205_RESET_CONTENT;\r
2021 case 206:\r
2022 return HTTP_STATUS_206_PARTIAL_CONTENT;\r
2023 case 300:\r
2024 return HTTP_STATUS_300_MULTIPLE_CHOICES;\r
2025 case 301:\r
2026 return HTTP_STATUS_301_MOVED_PERMANENTLY;\r
2027 case 302:\r
2028 return HTTP_STATUS_302_FOUND;\r
2029 case 303:\r
2030 return HTTP_STATUS_303_SEE_OTHER;\r
2031 case 304:\r
2032 return HTTP_STATUS_304_NOT_MODIFIED;\r
2033 case 305:\r
2034 return HTTP_STATUS_305_USE_PROXY;\r
2035 case 307:\r
2036 return HTTP_STATUS_307_TEMPORARY_REDIRECT;\r
2037 case 308:\r
2038 return HTTP_STATUS_308_PERMANENT_REDIRECT;\r
2039 case 400:\r
2040 return HTTP_STATUS_400_BAD_REQUEST;\r
2041 case 401:\r
2042 return HTTP_STATUS_401_UNAUTHORIZED;\r
2043 case 402:\r
2044 return HTTP_STATUS_402_PAYMENT_REQUIRED;\r
2045 case 403:\r
2046 return HTTP_STATUS_403_FORBIDDEN;\r
2047 case 404:\r
2048 return HTTP_STATUS_404_NOT_FOUND;\r
2049 case 405:\r
2050 return HTTP_STATUS_405_METHOD_NOT_ALLOWED;\r
2051 case 406:\r
2052 return HTTP_STATUS_406_NOT_ACCEPTABLE;\r
2053 case 407:\r
2054 return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;\r
2055 case 408:\r
2056 return HTTP_STATUS_408_REQUEST_TIME_OUT;\r
2057 case 409:\r
2058 return HTTP_STATUS_409_CONFLICT;\r
2059 case 410:\r
2060 return HTTP_STATUS_410_GONE;\r
2061 case 411:\r
2062 return HTTP_STATUS_411_LENGTH_REQUIRED;\r
2063 case 412:\r
2064 return HTTP_STATUS_412_PRECONDITION_FAILED;\r
2065 case 413:\r
2066 return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;\r
2067 case 414:\r
2068 return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;\r
2069 case 415:\r
2070 return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;\r
2071 case 416:\r
2072 return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;\r
2073 case 417:\r
2074 return HTTP_STATUS_417_EXPECTATION_FAILED;\r
2075 case 500:\r
2076 return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;\r
2077 case 501:\r
2078 return HTTP_STATUS_501_NOT_IMPLEMENTED;\r
2079 case 502:\r
2080 return HTTP_STATUS_502_BAD_GATEWAY;\r
2081 case 503:\r
2082 return HTTP_STATUS_503_SERVICE_UNAVAILABLE;\r
2083 case 504:\r
2084 return HTTP_STATUS_504_GATEWAY_TIME_OUT;\r
2085 case 505:\r
2086 return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;\r
2087\r
2088 default:\r
2089 return HTTP_STATUS_UNSUPPORTED_STATUS;\r
558b99a6
GB
2090 }\r
2091}\r
2092\r
2093/**\r
2094 Check whether header field called FieldName is in DeleteList.\r
2095\r
2096 @param[in] DeleteList Pointer to array of key/value header pairs.\r
2097 @param[in] DeleteCount The number of header pairs.\r
2098 @param[in] FieldName Pointer to header field's name.\r
2099\r
2100 @return TRUE if FieldName is not in DeleteList, that means this header field is valid.\r
2101 @return FALSE if FieldName is in DeleteList, that means this header field is invalid.\r
2102\r
2103**/\r
2104BOOLEAN\r
2105EFIAPI\r
2106HttpIsValidHttpHeader (\r
d1050b9d
MK
2107 IN CHAR8 *DeleteList[],\r
2108 IN UINTN DeleteCount,\r
2109 IN CHAR8 *FieldName\r
558b99a6
GB
2110 )\r
2111{\r
d1050b9d 2112 UINTN Index;\r
558b99a6 2113\r
0efeec8e
JW
2114 if (FieldName == NULL) {\r
2115 return FALSE;\r
2116 }\r
2117\r
558b99a6 2118 for (Index = 0; Index < DeleteCount; Index++) {\r
0efeec8e
JW
2119 if (DeleteList[Index] == NULL) {\r
2120 continue;\r
2121 }\r
d1102dba 2122\r
558b99a6
GB
2123 if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {\r
2124 return FALSE;\r
2125 }\r
2126 }\r
2127\r
2128 return TRUE;\r
2129}\r
2130\r
40c4cd54
AC
2131/**\r
2132 Create a HTTP_IO_HEADER to hold the HTTP header items.\r
2133\r
2134 @param[in] MaxHeaderCount The maximun number of HTTP header in this holder.\r
2135\r
2136 @return A pointer of the HTTP header holder or NULL if failed.\r
2137\r
2138**/\r
2139HTTP_IO_HEADER *\r
2140HttpIoCreateHeader (\r
d1050b9d 2141 UINTN MaxHeaderCount\r
40c4cd54
AC
2142 )\r
2143{\r
d1050b9d 2144 HTTP_IO_HEADER *HttpIoHeader;\r
40c4cd54
AC
2145\r
2146 if (MaxHeaderCount == 0) {\r
2147 return NULL;\r
2148 }\r
2149\r
2150 HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));\r
2151 if (HttpIoHeader == NULL) {\r
2152 return NULL;\r
2153 }\r
2154\r
2155 HttpIoHeader->MaxHeaderCount = MaxHeaderCount;\r
d1050b9d 2156 HttpIoHeader->Headers = (EFI_HTTP_HEADER *)(HttpIoHeader + 1);\r
40c4cd54
AC
2157\r
2158 return HttpIoHeader;\r
2159}\r
2160\r
2161/**\r
2162 Destroy the HTTP_IO_HEADER and release the resources.\r
2163\r
2164 @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed.\r
2165\r
2166**/\r
2167VOID\r
2168HttpIoFreeHeader (\r
d1050b9d 2169 IN HTTP_IO_HEADER *HttpIoHeader\r
40c4cd54
AC
2170 )\r
2171{\r
d1050b9d 2172 UINTN Index;\r
40c4cd54
AC
2173\r
2174 if (HttpIoHeader != NULL) {\r
2175 if (HttpIoHeader->HeaderCount != 0) {\r
2176 for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {\r
2177 FreePool (HttpIoHeader->Headers[Index].FieldName);\r
2178 ZeroMem (HttpIoHeader->Headers[Index].FieldValue, AsciiStrSize (HttpIoHeader->Headers[Index].FieldValue));\r
2179 FreePool (HttpIoHeader->Headers[Index].FieldValue);\r
2180 }\r
2181 }\r
d1050b9d 2182\r
40c4cd54
AC
2183 FreePool (HttpIoHeader);\r
2184 }\r
2185}\r
2186\r
2187/**\r
2188 Set or update a HTTP header with the field name and corresponding value.\r
2189\r
2190 @param[in] HttpIoHeader Point to the HTTP header holder.\r
2191 @param[in] FieldName Null terminated string which describes a field name.\r
2192 @param[in] FieldValue Null terminated string which describes the corresponding field value.\r
2193\r
2194 @retval EFI_SUCCESS The HTTP header has been set or updated.\r
2195 @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
2196 @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation.\r
2197 @retval Other Unexpected error happened.\r
2198\r
2199**/\r
2200EFI_STATUS\r
2201HttpIoSetHeader (\r
d1050b9d
MK
2202 IN HTTP_IO_HEADER *HttpIoHeader,\r
2203 IN CHAR8 *FieldName,\r
2204 IN CHAR8 *FieldValue\r
40c4cd54
AC
2205 )\r
2206{\r
d1050b9d
MK
2207 EFI_HTTP_HEADER *Header;\r
2208 UINTN StrSize;\r
2209 CHAR8 *NewFieldValue;\r
40c4cd54 2210\r
d1050b9d 2211 if ((HttpIoHeader == NULL) || (FieldName == NULL) || (FieldValue == NULL)) {\r
40c4cd54
AC
2212 return EFI_INVALID_PARAMETER;\r
2213 }\r
2214\r
2215 Header = HttpFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);\r
2216 if (Header == NULL) {\r
2217 //\r
2218 // Add a new header.\r
2219 //\r
2220 if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {\r
2221 return EFI_OUT_OF_RESOURCES;\r
2222 }\r
d1050b9d 2223\r
40c4cd54
AC
2224 Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];\r
2225\r
d1050b9d 2226 StrSize = AsciiStrSize (FieldName);\r
40c4cd54
AC
2227 Header->FieldName = AllocatePool (StrSize);\r
2228 if (Header->FieldName == NULL) {\r
2229 return EFI_OUT_OF_RESOURCES;\r
2230 }\r
d1050b9d 2231\r
40c4cd54
AC
2232 CopyMem (Header->FieldName, FieldName, StrSize);\r
2233 Header->FieldName[StrSize -1] = '\0';\r
2234\r
d1050b9d 2235 StrSize = AsciiStrSize (FieldValue);\r
40c4cd54
AC
2236 Header->FieldValue = AllocatePool (StrSize);\r
2237 if (Header->FieldValue == NULL) {\r
2238 FreePool (Header->FieldName);\r
2239 return EFI_OUT_OF_RESOURCES;\r
2240 }\r
d1050b9d 2241\r
40c4cd54
AC
2242 CopyMem (Header->FieldValue, FieldValue, StrSize);\r
2243 Header->FieldValue[StrSize -1] = '\0';\r
2244\r
2245 HttpIoHeader->HeaderCount++;\r
2246 } else {\r
2247 //\r
2248 // Update an existing one.\r
2249 //\r
d1050b9d 2250 StrSize = AsciiStrSize (FieldValue);\r
40c4cd54
AC
2251 NewFieldValue = AllocatePool (StrSize);\r
2252 if (NewFieldValue == NULL) {\r
2253 return EFI_OUT_OF_RESOURCES;\r
2254 }\r
d1050b9d 2255\r
40c4cd54
AC
2256 CopyMem (NewFieldValue, FieldValue, StrSize);\r
2257 NewFieldValue[StrSize -1] = '\0';\r
2258\r
2259 if (Header->FieldValue != NULL) {\r
2260 FreePool (Header->FieldValue);\r
2261 }\r
d1050b9d 2262\r
40c4cd54
AC
2263 Header->FieldValue = NewFieldValue;\r
2264 }\r
2265\r
2266 return EFI_SUCCESS;\r
2267}\r