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