Azure IoT common library

Dependents:   STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more

Revision:
43:00b607807827
Parent:
25:8507bf644fdf
Child:
49:6bb8b9a66642
--- 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;
+}