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