/* ********************************************************************************************************* * uC/HTTP * Hypertext Transfer Protocol * * Copyright 2004-2020 Silicon Laboratories Inc. www.silabs.com * * SPDX-License-Identifier: APACHE-2.0 * * This software is subject to an open source license and is distributed by * Silicon Laboratories Inc. pursuant to the terms of the Apache License, * Version 2.0 available at www.apache.org/licenses/LICENSE-2.0. * ********************************************************************************************************* */ /* ********************************************************************************************************* * * HTTP * * Filename : http.c * Version : V3.01.00 ********************************************************************************************************* */ /* ********************************************************************************************************* ********************************************************************************************************* * INCLUDE FILES ********************************************************************************************************* ********************************************************************************************************* */ #define MICRIUM_SOURCE #define HTTP_MODULE #include "http.h" #include "http_dict.h" /* ********************************************************************************************************* ********************************************************************************************************* * LOCAL FUNCTION PROTOTYPES ********************************************************************************************************* ********************************************************************************************************* */ static void HTTP_CharToStrHex(CPU_CHAR character, CPU_CHAR *p_str_char); static CPU_CHAR HTTP_StrHexToChar(const CPU_CHAR *p_str_char); #if 0 static CPU_INT08U HTTP_StrSizeHexDigReq(CPU_INT32U nbr); #endif static CPU_CHAR *HTTP_StrFileExtGet(CPU_CHAR *p_file_name, CPU_SIZE_T path_len); /* ********************************************************************************************************* * HTTP_HdrParseFieldValueGet() * * Description : Get the beginning of a field value. * * Argument(s) : p_field Pointer to the beginning of the field line. * * field_len Field length. * * p_field_end Pointer to the end of the field line. * * p_len_rem Pointer to a variable that will receive the remaining length. * * Return(s) : Pointer to the beginning of the field value. * * Caller(s) : Various. * * Note(s) : (1) RFC #2616, section "4.2 Message Headers" describe how Header field should be formated. * * (a) HTTP header fields, which include general-header (section 4.5), request-header * (section 5.3), response-header (section 6.2), and entity-header (section 7.1) * fields, follow the same generic format as that given in Section 3.1 of RFC 822 [9]. * Each header field consists of a name followed by a colon (":") and the field value. * Field names are case-insensitive. The field value MAY be preceded by any amount of * LWS, though a single SP is preferred. Header fields can be extended over multiple * lines by preceding each extra line with at least one SP or HT. Applications ought * to follow "common form", where one is known or indicated, when generating HTTP * constructs, since there might exist some implementations that fail to accept * anything * * beyond the common forms. * * message-header = field-name ":" [ field-value ] * field-name = token * field-value = *( field-content | LWS ) * field-content = ********************************************************************************************************* */ CPU_CHAR *HTTP_HdrParseFieldValueGet(CPU_CHAR *p_field, CPU_INT16U field_len, CPU_CHAR *p_field_end, CPU_INT16U *p_len_rem) { CPU_INT16U len; CPU_CHAR *p_val; p_val = p_field + field_len; len = (p_field_end - p_val); p_val = Str_Char_N(p_val, len, ASCII_CHAR_COLON); /* Field val located after ':' (see Note #1a). */ p_val++; len = (p_field_end - p_val); p_val = HTTP_StrGraphSrchFirst(p_val, len); /* Remove blank space before field value. */ *p_len_rem = (p_field_end - p_val); return (p_val); } /* ********************************************************************************************************* * HTTP_StrGraphSrchFirst() * * Description : Get pointer to the first graphic character of a string. * * Argument(s) : p_str Pointer to the string to search in. * * str_len String length. * * Return(s) : Pointer to the first graphic character, if successfully found. * * DEF_NULL, otherwise. * * Caller(s) : Various. * * Note(s) : none. ********************************************************************************************************* */ CPU_CHAR *HTTP_StrGraphSrchFirst(CPU_CHAR *p_str, CPU_INT32U str_len) { CPU_CHAR *p_char; CPU_INT32U len_rem; len_rem = str_len; p_char = p_str; while ((ASCII_IS_GRAPH(*p_char) == DEF_NO) && (len_rem > 0)) { p_char++; len_rem--; } if (len_rem == 0) { return ((CPU_CHAR *)0); } return (p_char); } /* ********************************************************************************************************* * HTTP_URL_EncodeStr() * * Description : Perform URL encoding on the given string. * * Argument(s) : p_str_src Pointer to source string to encode. * * p_str_dest Pointer to destination string where the encoded string will be copied. * * p_str_len Argument used to pass length of the source string and * to return length of the encoded destination string. * * str_len_max Maximum length of the encoded string. * * Return(s) : DEF_OK, if encoding succeeded. * * DEF_FAIL, otherwise * * Caller(s) : Various. * * Note(s) : None. ********************************************************************************************************* */ CPU_BOOLEAN HTTP_URL_EncodeStr(const CPU_CHAR *p_str_src, CPU_CHAR *p_str_dest, CPU_SIZE_T *p_str_len, CPU_SIZE_T str_len_max) { CPU_BOOLEAN alpha_num; CPU_BOOLEAN unreserved; CPU_BOOLEAN result; CPU_INT16U len; CPU_INT16U len_copy; CPU_INT16U ctr; /* -------- VALIDATE DESTINATION BUFFER LENGTH -------- */ ctr = 0u; for (len = 0u; len < *p_str_len; len++) { alpha_num = ASCII_IsAlphaNum(*p_str_src); /* Check if char is alphabetic or numeric. */ switch (*p_str_src) { /* Check if char is an unreserved character. */ case ASCII_CHAR_FULL_STOP: case ASCII_CHAR_HYPHEN_MINUS: case ASCII_CHAR_TILDE: case ASCII_CHAR_LOW_LINE: unreserved = DEF_YES; break; default: unreserved = DEF_NO; break; } if ((alpha_num == DEF_NO) && (unreserved == DEF_NO)) { ctr++; /* Increment number of char to encode. */ } p_str_src++; } len = *p_str_len + HTTP_URL_ENCODING_JUMP * ctr; if (len > str_len_max) { len_copy = 0u; result = DEF_FAIL; goto exit; } /* --------- COPY URL ENCODING STRING BUFFER ---------- */ p_str_src -= *p_str_len; /* Reset pointer of source string. */ len_copy = 0u; for (len = 0u; len < *p_str_len; len++) { alpha_num = ASCII_IsAlphaNum(*p_str_src); /* Check if char is alphabetic or numeric. */ switch (*p_str_src) { /* Check if char is an unreserved character. */ case ASCII_CHAR_FULL_STOP: case ASCII_CHAR_HYPHEN_MINUS: case ASCII_CHAR_TILDE: case ASCII_CHAR_LOW_LINE: unreserved = DEF_YES; break; default: unreserved = DEF_NO; break; } if ((alpha_num == DEF_YES) || (unreserved == DEF_YES)) { *p_str_dest = *p_str_src; len_copy++; } else { HTTP_CharToStrHex(*p_str_src, p_str_dest); /* Convert char to its %-encoding equivalent. */ if (*p_str_dest != ASCII_CHAR_NULL) { len_copy += HTTP_URL_ENCODING_LEN; p_str_dest += HTTP_URL_ENCODING_JUMP; } else { result = DEF_FAIL; goto exit; } } p_str_src++; p_str_dest++; } result = DEF_OK; exit: *p_str_len = len_copy; return (result); } /* ********************************************************************************************************* * HTTP_URL_DecodeStr() * * Description : Perform URL decoding on the given string. * * Argument(s) : p_str_src Pointer to source string to decode. * * p_str_dest Pointer to destination string where the decoded string will be copied. * * p_str_len Argument used to pass the length of the source string and * to return the length of the decoded destination string. * * Return(s) : DEF_OK, if decoding succeeded. * DEF_FAIL, otherwise. * * Caller(s) : Various. * * Note(s) : None. ********************************************************************************************************* */ CPU_BOOLEAN HTTP_URL_DecodeStr(const CPU_CHAR *p_str_src, CPU_CHAR *p_str_dest, CPU_SIZE_T *p_str_len) { CPU_CHAR char_tmp; CPU_INT16U len; CPU_INT16U len_copy; CPU_BOOLEAN result; len_copy = 0; for (len = 0u; len < *p_str_len; len++) { if (*p_str_src == ASCII_CHAR_PERCENTAGE_SIGN) { char_tmp = HTTP_StrHexToChar(p_str_src); if (char_tmp != ASCII_CHAR_NULL) { *p_str_dest = char_tmp; len_copy++; p_str_src += HTTP_URL_ENCODING_JUMP; len += HTTP_URL_ENCODING_JUMP; } else { result = DEF_FAIL; goto exit; } } else if (*p_str_src == ASCII_CHAR_PLUS_SIGN) { *p_str_dest = ASCII_CHAR_SPACE; len_copy++; } else { *p_str_dest = *p_str_src; len_copy++; } p_str_src++; p_str_dest++; } result = DEF_OK; exit: *p_str_len = len_copy; return (result); } /* ********************************************************************************************************* * HTTP_URL_DecodeReplaceStr() * * Description : $$$$ Add function description. * * Argument(s) : p_str Pointer to the string to scan and replace characters. * * p_str_len Argument used to pass the length of the source string and * to return the length of the decoded destination string. * * Return(s) : $$$$ Add return value description. * * Caller(s) : none. * * Note(s) : none. ********************************************************************************************************* */ CPU_BOOLEAN HTTP_URL_DecodeReplaceStr(CPU_CHAR *p_str, CPU_INT16U *p_str_len) { CPU_CHAR *p_str_char; CPU_CHAR *p_str_copy_src; CPU_CHAR *p_str_copy_dest; CPU_INT32U len; CPU_INT32U len_copy; CPU_BOOLEAN result; p_str_char = p_str; len = *p_str_len; p_str[*p_str_len] = ASCII_CHAR_NULL; /* Make a sure to create a str. */ while (*p_str_char != ASCII_CHAR_NULL) { if (*p_str_char == ASCII_CHAR_PLUS_SIGN) { /* See Note #1a. */ *p_str_char = ASCII_CHAR_SPACE; } else if (*p_str_char == ASCII_CHAR_PERCENTAGE_SIGN) { /* See Note #1b. */ *p_str_char = HTTP_StrHexToChar(p_str_char); /* Convert Hex str to 1 char. */ if (*p_str_char == ASCII_CHAR_NULL) { result = DEF_FAIL; goto exit; } p_str_copy_dest = p_str_char + 1; /* Rem str must be moved after the char. */ p_str_copy_src = p_str_char + 3; /* Rem str is located after hex str (3 char). */ len_copy = len - 2; Str_Copy_N(p_str_copy_dest, /* Copy rem str. */ p_str_copy_src, len_copy); *p_str_len -= 2; /* Str has lost 2 char. */ } p_str_char++; /* Move the next char. */ len--; } result = DEF_OK; exit: return (result); } /* ********************************************************************************************************* * HTTP_URL_CharEncodeNbr() * * Description : Calculate number of characters that would be encoded if an URL encoding is performed on the * given string. * * Argument(s) : p_str_src Pointer to string that would be encoded. * * str_len Length of string. * * Return(s) : Number of character that would be encoded. * * Caller(s) : Various. * * Note(s) : None. ********************************************************************************************************* */ CPU_INT16U HTTP_URL_CharEncodeNbr(const CPU_CHAR *p_str_src, CPU_SIZE_T str_len) { CPU_BOOLEAN alpha_num; CPU_BOOLEAN unreserved; CPU_INT16U ctr; CPU_INT16U len; ctr = 0u; for (len = 0u; len < str_len; len++) { alpha_num = ASCII_IsAlphaNum(*p_str_src); /* Check if char is alphabetic or numeric. */ switch (*p_str_src) { /* Check if char is an unreserved character. */ case ASCII_CHAR_FULL_STOP: case ASCII_CHAR_HYPHEN_MINUS: case ASCII_CHAR_TILDE: case ASCII_CHAR_LOW_LINE: unreserved = DEF_YES; break; default: unreserved = DEF_NO; break; } if ((alpha_num == DEF_NO) && (unreserved == DEF_NO)) { ctr++; /* Increment number of char to encode. */ } p_str_src++; } return (ctr); } /* ********************************************************************************************************* * HTTP_ChunkTransferWrSize() * * Description : Write to buffer the chunk size value in hexadecimal representation, plus the CRLF characters. * * Argument(s) : p_buf_wr Pointer to buffer in which to write chunk size value. * * buf_len Buffer length remaining. * * buf_len_max Maximum buffer length. * * size Chunk size value to write. * * Return(s) : Pointer to end of written data in buffer. * * Caller(s) : Various. * * Note(s) : None. ********************************************************************************************************* */ CPU_CHAR *HTTP_ChunkTransferWrSize(CPU_CHAR *p_buf_wr, CPU_SIZE_T buf_len, CPU_SIZE_T nbr_dig_max, CPU_INT16U size) { CPU_CHAR *p_str; CPU_SIZE_T size_max; size_max = nbr_dig_max + STR_CR_LF_LEN; if (buf_len < size_max) { return (DEF_NULL); } p_str = Str_FmtNbr_Int32U(size, nbr_dig_max, DEF_NBR_BASE_HEX, '0', DEF_YES, DEF_NO, p_buf_wr); if (p_str == DEF_NULL) { return (DEF_NULL); } p_str += nbr_dig_max; Str_Copy_N(p_str, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { return (DEF_NULL); } p_str += STR_CR_LF_LEN; return (p_str); } /* ********************************************************************************************************* * HTTP_StrSizeHexDigReq() * * Description : Find the number of digital characters needed to represent in hexadecimal the given number. * * Argument(s) : nbr Number to convert in hexadecimal. * * Return(s) : Number of characters needed for conversion. * * Caller(s) : HTTP_ChunkTransferWrSize(). * * Note(s) : None. ********************************************************************************************************* */ CPU_INT08U HTTP_StrSizeHexDigReq(CPU_INT32U nbr) { if (nbr <= 0x000Fu) { return (1); } else if (nbr <= 0x00FFu) { return (2); } else if (nbr <= 0x0FFFu) { return (3); } else if (nbr <= 0xFFFFu) { return (4); } else if (nbr <= 0xFFFFFu) { return (5); } else if (nbr <= 0xFFFFFFu) { return (6); } else if (nbr <= 0xFFFFFFFu) { return (7); } else { return (8); } } /* ********************************************************************************************************* * HTTP_GetContentTypeFromFileExt() * * Description : Get the HTTP Content Type from the extension of the given filename string. * * Argument(s) : p_file_path Pointer to filename string. * * path_len_max Length of the filename string. * * Return(s) : HTTP Content Type. * * Caller(s) : Various. * * Note(s) : None. ********************************************************************************************************* */ HTTP_CONTENT_TYPE HTTP_GetContentTypeFromFileExt(CPU_CHAR *p_file_path, CPU_SIZE_T path_len_max) { CPU_CHAR *p_ext; CPU_INT32U content_type; p_ext = HTTP_StrFileExtGet(p_file_path, path_len_max); /* Srch file ext. */ if (p_ext == DEF_NULL) { return (HTTP_CONTENT_TYPE_OCTET_STREAM); } /* Get content type based on the file ext. */ content_type = HTTP_Dict_KeyGet(HTTP_Dict_FileExt, HTTP_Dict_FileExtSize, p_ext, DEF_YES, path_len_max); if (content_type == HTTP_DICT_KEY_INVALID) { return (HTTP_CONTENT_TYPE_OCTET_STREAM); } return ((HTTP_CONTENT_TYPE)content_type); } /* ********************************************************************************************************* ********************************************************************************************************* * LOCAL FUNCTIONS ********************************************************************************************************* ********************************************************************************************************* */ /* ********************************************************************************************************* * HTTP_CharToStrHex() * * Description : Get the hexadecimal code of a ASCII character in form of a string. * * Argument(s) : character Character from which to get the hexadecimal code. * * p_str_char Pointer to string that will received the hexademacimal code. * * String will be ASCII_CHAR_NULL in case of error. * * Return(s) : None. * * Caller(s) : HTTP_URL_EndodeStr(). * * Note(s) : None. ********************************************************************************************************* */ static void HTTP_CharToStrHex(CPU_CHAR character, CPU_CHAR *p_str_char) { CPU_CHAR char_tmp; CPU_INT08U shift; CPU_INT08U i; *p_str_char = ASCII_CHAR_PERCENTAGE_SIGN; p_str_char++; shift = DEF_NIBBLE_NBR_BITS; for (i = 0; i < 2; i++) { char_tmp = (character >> shift) & 0x0F; if (char_tmp <= 0x09) { char_tmp += 0x30; } else if ((char_tmp >= 0x0A) && (char_tmp <= 0x0F)) { char_tmp -= 10; char_tmp += 0x41; } else { p_str_char--; p_str_char = ASCII_CHAR_NULL; return; } shift -= DEF_NIBBLE_NBR_BITS; *p_str_char = char_tmp; p_str_char++; } } /* ********************************************************************************************************* * HTTP_StrHexToChar() * * Description : Convert an hexadecimal code string to its related ASCII character. * * Argument(s) : pstr_char Pointer to string with hexadecimal code to convert. * * Return(s) : The ASCII character, if conversion succeeded. * * The Null character, otherwise. * * Caller(s) : HTTP_URL_DecodeStr(). * * Note(s) : None. ********************************************************************************************************* */ static CPU_CHAR HTTP_StrHexToChar(const CPU_CHAR *p_str_char) { CPU_CHAR char_rtn; const CPU_CHAR *p_char; CPU_INT08U shift; CPU_INT08U i; if (*p_str_char != ASCII_CHAR_PERCENTAGE_SIGN) { /* First char must be '%', See note #1. */ return (ASCII_CHAR_NULL); } p_char = p_str_char + 1; char_rtn = ASCII_CHAR_NULL; shift = DEF_NIBBLE_NBR_BITS; for (i = 0; i < 2; i++) { if ((*p_char >= ASCII_CHAR_DIGIT_ZERO) && (*p_char <= ASCII_CHAR_DIGIT_NINE)) { char_rtn += (*p_char - ASCII_CHAR_DIGIT_ZERO); } else if ((*p_char >= ASCII_CHAR_LATIN_UPPER_A) && (*p_char <= ASCII_CHAR_LATIN_UPPER_F)) { char_rtn += (*p_char - ASCII_CHAR_LATIN_UPPER_A) + 10u; } else if ((*p_char >= ASCII_CHAR_LATIN_LOWER_A) && (*p_char <= ASCII_CHAR_LATIN_LOWER_F)) { char_rtn += (*p_char - ASCII_CHAR_LATIN_LOWER_A) + 10u; } else { return (ASCII_CHAR_NULL); } p_char++; char_rtn <<= shift; /* First char is the upper part of the conv char. */ shift -= DEF_NIBBLE_NBR_BITS; /* Next char is the lower part. */ } return (char_rtn); } /* ********************************************************************************************************* * HTTP_StrFileExtGet() * * Description : Get the file extension characters from the given filename. * * Argument(s) : p_file_name Pointer to filename string. * * path_len Length of the filename string. * * Return(s) : Pointer to begin of the extension in the given filename string. * * Caller(s) : HTTP_GetContentTypeFromFileExt(). * * Note(s) : None. ********************************************************************************************************* */ static CPU_CHAR *HTTP_StrFileExtGet(CPU_CHAR *p_file_name, CPU_SIZE_T path_len) { CPU_CHAR *p_last_dot; p_last_dot = Str_Char_Last_N(p_file_name, path_len, ASCII_CHAR_FULL_STOP); if (p_last_dot != DEF_NULL) { p_last_dot++; } return (p_last_dot); }