Azure IoT common library
Dependents: STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more
Diff: urlencode.c
- Revision:
- 43:00b607807827
- Parent:
- 25:8507bf644fdf
- Child:
- 49:6bb8b9a66642
diff -r 0cc3c211ad26 -r 00b607807827 urlencode.c --- a/urlencode.c Tue Mar 20 10:31:23 2018 -0700 +++ b/urlencode.c Mon Apr 16 14:27:59 2018 -0700 @@ -8,8 +8,15 @@ #include "azure_c_shared_utility/urlencode.h" #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/crt_abstractions.h" -#define NIBBLE_STR(c) (char)(c < 10 ? c + '0' : c - 10 + 'a') +#define NIBBLE_TO_STRING(c) (char)((c) < 10 ? (c) + '0' : (c) - 10 + 'a') +#define NIBBLE_FROM_STRING(c) (char)(ISDIGIT(c) ? (c) - '0' : TOUPPER(c) + 10 - 'A') +#define IS_HEXDIGIT(c) ( \ + ((c >= '0') && (c <= '9')) || \ + ((c >= 'A') && (c <= 'F')) || \ + ((c >= 'a') && (c <= 'f')) \ +) #define IS_PRINTABLE(c) ( \ (c == 0) || \ (c == '!') || \ @@ -21,6 +28,29 @@ ((c >= 'a') && (c <= 'z')) \ ) +/*The below macros are to be called on the big nibble of a hex value*/ +#define IS_IN_ASCII_RANGE(c) ( \ + (c >= '0') && (c <= '7') \ +) +#define IS_IN_EXTENDED_ASCII_RANGE(c) ( \ + ((c >= '8') && (c <= '9')) || \ + ((c >= 'A') && (c <= 'F')) || \ + ((c >= 'a') && (c <= 'f')) \ +) +#define IS_IN_CONTINUATION_BYTE_RANGE(c) ( \ + (c == '8') || (c == '9') || \ + (c == 'A') || (c == 'B') || \ + (c == 'a') || (c == 'b') \ +) +#define IS_IN_LEADING_BYTE_RANGE(c) ( \ + ((c >= 'C') && (c <= 'F')) || \ + ((c >= 'c') && (c <= 'f')) \ +) +#define IS_IN_UNSUPPORTED_LEADING_BYTE_RANGE(c) ( \ + ((c >= 'D') && (c <= 'F')) || \ + ((c >= 'd') && (c <= 'f')) \ +) + static size_t URL_PrintableChar(unsigned char charVal, char* buffer) { size_t size; @@ -41,8 +71,8 @@ bigNibbleVal -= 0x04; } - bigNibbleStr = NIBBLE_STR(bigNibbleVal); - littleNibbleStr = NIBBLE_STR(littleNibbleVal); + bigNibbleStr = NIBBLE_TO_STRING(bigNibbleVal); + littleNibbleStr = NIBBLE_TO_STRING(littleNibbleVal); buffer[0] = '%'; @@ -73,6 +103,100 @@ return size; } +static size_t calculateDecodedStringSize(const char* encodedString, size_t len) +{ + size_t decodedSize = 0; + + if (encodedString == NULL) + { + LogError("Null encoded string"); + } + else if (len == 0) + { + decodedSize = 1; //for null terminator + } + else + { + size_t remaining_len = len; + size_t next_step = 0; + size_t i = 0; + while (i < len) + { + //percent encoded character + if (encodedString[i] == '%') + { + if (remaining_len < 3 || !IS_HEXDIGIT(encodedString[i+1]) || !IS_HEXDIGIT(encodedString[i+2])) + { + LogError("Incomplete or invalid percent encoding"); + break; + } + else if (!IS_IN_ASCII_RANGE(encodedString[i+1])) + { + LogError("Out of range of characters accepted by this decoder"); + break; + } + else + { + decodedSize++; + next_step = 3; + } + } + else if (!IS_PRINTABLE(encodedString[i])) + { + LogError("Unprintable value in encoded string"); + break; + } + //safe character + else + { + decodedSize++; + next_step = 1; + } + + i += next_step; + remaining_len -= next_step; + } + + if (encodedString[i] != '\0') //i.e. broke out of above loop due to error + { + decodedSize = 0; + } + else + { + decodedSize++; //add space for the null terminator + } + } + return decodedSize; +} + +static unsigned char charFromNibbles(char bigNibbleStr, char littleNibbleStr) +{ + unsigned char bigNibbleVal = NIBBLE_FROM_STRING(bigNibbleStr); + unsigned char littleNibbleVal = NIBBLE_FROM_STRING(littleNibbleStr); + + return bigNibbleVal << 4 | littleNibbleVal; +} + +static void createDecodedString(const char* input, size_t input_size, char* output) +{ + /* Note that there is no danger of reckless indexing here, as calculateDecodedStringSize() + has already checked lengths of strings to ensure the formatting is always correct*/ + size_t i = 0; + while (i <= input_size) //the <= instead of < ensures the '\0' will be copied + { + if (input[i] != '%') + { + *output++ = input[i]; + i++; + } + else + { + *output++ = charFromNibbles(input[i+1], input[i+2]); + i += 3; + } + } +} + static size_t URL_PrintableCharSize(unsigned char charVal) { size_t size; @@ -165,3 +289,64 @@ } return result; } + +STRING_HANDLE URL_DecodeString(const char* textDecode) +{ + STRING_HANDLE result; + if (textDecode == NULL) + { + result = NULL; + } + else + { + STRING_HANDLE tempString = STRING_construct(textDecode); + if (tempString == NULL) + { + result = NULL; + } + else + { + result = URL_Decode(tempString); + STRING_delete(tempString); + } + } + return result; +} + +STRING_HANDLE URL_Decode(STRING_HANDLE input) +{ + STRING_HANDLE result; + if (input == NULL) + { + LogError("URL_Decode:: NULL input"); + result = NULL; + } + else + { + size_t decodedStringSize; + char* decodedString; + const char* inputString = STRING_c_str(input); + size_t inputLen = strlen(inputString); + if ((decodedStringSize = calculateDecodedStringSize(inputString, inputLen)) == 0) + { + LogError("URL_Decode:: Invalid input string"); + result = NULL; + } + else if ((decodedString = (char*)malloc(decodedStringSize)) == NULL) + { + LogError("URL_Decode:: MALLOC failure on decode."); + result = NULL; + } + else + { + createDecodedString(inputString, inputLen, decodedString); + result = STRING_new_with_memory(decodedString); + if (result == NULL) + { + LogError("URL_Decode:: MALLOC failure on decode"); + free(decodedString); + } + } + } + return result; +}