Azure IoT / azure_c_shared_utility

Dependents:   STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers urlencode.c Source File

urlencode.c

00001 // Copyright (c) Microsoft. All rights reserved.
00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
00003 
00004 #include <stdlib.h>
00005 #include <stddef.h>
00006 #include <string.h>
00007 #include "azure_c_shared_utility/gballoc.h"
00008 #include "azure_c_shared_utility/urlencode.h"
00009 #include "azure_c_shared_utility/xlogging.h"
00010 #include "azure_c_shared_utility/strings.h"
00011 #include "azure_c_shared_utility/crt_abstractions.h"
00012 
00013 #define NIBBLE_TO_STRING(c) (char)((c) < 10 ? (c) + '0' : (c) - 10 + 'a')
00014 #define NIBBLE_FROM_STRING(c) (char)(ISDIGIT(c) ? (c) - '0' : TOUPPER(c) + 10 - 'A')
00015 #define IS_HEXDIGIT(c) (            \
00016     ((c >= '0') && (c <= '9')) ||   \
00017     ((c >= 'A') && (c <= 'F')) ||   \
00018     ((c >= 'a') && (c <= 'f'))      \
00019 )
00020 #define IS_PRINTABLE(c) (                           \
00021     (c == 0) ||                                     \
00022     (c == '!') ||                                   \
00023     (c == '(') || (c == ')') || (c == '*') ||       \
00024     (c == '-') || (c == '.') ||                     \
00025     ((c >= '0') && (c <= '9')) ||                   \
00026     ((c >= 'A') && (c <= 'Z')) ||                   \
00027     (c == '_') ||                                   \
00028     ((c >= 'a') && (c <= 'z'))                      \
00029 )
00030 
00031 /*The below macros are to be called on the big nibble of a hex value*/
00032 #define IS_IN_ASCII_RANGE(c) (  \
00033     (c >= '0') && (c <= '7')    \
00034 )
00035 #define IS_IN_EXTENDED_ASCII_RANGE(c) ( \
00036     ((c >= '8') && (c <= '9')) ||       \
00037     ((c >= 'A') && (c <= 'F')) ||       \
00038     ((c >= 'a') && (c <= 'f'))          \
00039 )
00040 #define IS_IN_CONTINUATION_BYTE_RANGE(c) (  \
00041     (c == '8') || (c == '9') ||             \
00042     (c == 'A') || (c == 'B') ||             \
00043     (c == 'a') || (c == 'b')                \
00044 )
00045 #define IS_IN_LEADING_BYTE_RANGE(c) (   \
00046     ((c >= 'C') && (c <= 'F')) ||       \
00047     ((c >= 'c') && (c <= 'f'))          \
00048 )
00049 #define IS_IN_UNSUPPORTED_LEADING_BYTE_RANGE(c) (   \
00050     ((c >= 'D') && (c <= 'F')) ||                   \
00051     ((c >= 'd') && (c <= 'f'))                      \
00052 )
00053 
00054 static size_t URL_PrintableChar(unsigned char charVal, char* buffer)
00055 {
00056     size_t size;
00057     if (IS_PRINTABLE(charVal))
00058     {
00059         buffer[0] = (char)charVal;
00060         size = 1;
00061     }
00062     else
00063     {
00064         char bigNibbleStr;
00065         char littleNibbleStr;
00066         unsigned char bigNibbleVal = charVal >> 4;
00067         unsigned char littleNibbleVal = charVal & 0x0F;
00068 
00069         if (bigNibbleVal >= 0x0C)
00070         {
00071             bigNibbleVal -= 0x04;
00072         }
00073 
00074         bigNibbleStr = NIBBLE_TO_STRING(bigNibbleVal);
00075         littleNibbleStr = NIBBLE_TO_STRING(littleNibbleVal);
00076 
00077         buffer[0] = '%';
00078 
00079         if (charVal < 0x80)
00080         {
00081             buffer[1] = bigNibbleStr;
00082             buffer[2] = littleNibbleStr;
00083             size = 3;
00084         }
00085         else
00086         {
00087             buffer[1] = 'c';
00088             buffer[3] = '%';
00089             buffer[4] = bigNibbleStr;
00090             buffer[5] = littleNibbleStr;
00091             if (charVal < 0xC0)
00092             {
00093                 buffer[2] = '2';
00094             }
00095             else
00096             {
00097                 buffer[2] = '3';
00098             }
00099             size = 6;
00100         }
00101     }
00102 
00103     return size;
00104 }
00105 
00106 static size_t calculateDecodedStringSize(const char* encodedString, size_t len)
00107 {
00108     size_t decodedSize = 0;
00109 
00110     if (encodedString == NULL)
00111     {
00112         LogError("Null encoded string");
00113     }
00114     else if (len == 0)
00115     {
00116         decodedSize = 1; //for null terminator
00117     }
00118     else
00119     {
00120         size_t remaining_len = len;
00121         size_t next_step = 0;
00122         size_t i = 0;
00123         while (i < len)
00124         {
00125             //percent encoded character
00126             if (encodedString[i] == '%')
00127             {
00128                 if (remaining_len < 3 || !IS_HEXDIGIT(encodedString[i+1]) || !IS_HEXDIGIT(encodedString[i+2]))
00129                 {
00130                     LogError("Incomplete or invalid percent encoding");
00131                     break;
00132                 }
00133                 else if (!IS_IN_ASCII_RANGE(encodedString[i+1]))
00134                 {
00135                     LogError("Out of range of characters accepted by this decoder");
00136                     break;
00137                 }
00138                 else
00139                 {
00140                     decodedSize++;
00141                     next_step = 3;
00142                 }
00143             }
00144             else if (!IS_PRINTABLE(encodedString[i]))
00145             {
00146                 LogError("Unprintable value in encoded string");
00147                 break;
00148             }
00149             //safe character
00150             else
00151             {
00152                 decodedSize++;
00153                 next_step = 1;
00154             }
00155 
00156             i += next_step;
00157             remaining_len -= next_step;
00158         }
00159 
00160         if (encodedString[i] != '\0') //i.e. broke out of above loop due to error
00161         {
00162             decodedSize = 0;
00163         }
00164         else
00165         {
00166             decodedSize++; //add space for the null terminator
00167         }
00168     }
00169     return decodedSize;
00170 }
00171 
00172 static unsigned char charFromNibbles(char bigNibbleStr, char littleNibbleStr)
00173 {
00174     unsigned char bigNibbleVal = NIBBLE_FROM_STRING(bigNibbleStr);
00175     unsigned char littleNibbleVal = NIBBLE_FROM_STRING(littleNibbleStr);
00176 
00177     return bigNibbleVal << 4 | littleNibbleVal;
00178 }
00179 
00180 static void createDecodedString(const char* input, size_t input_size, char* output)
00181 {
00182     /* Note that there is no danger of reckless indexing here, as calculateDecodedStringSize()
00183     has already checked lengths of strings to ensure the formatting is always correct*/
00184     size_t i = 0;
00185     while (i <= input_size) //the <= instead of < ensures the '\0' will be copied
00186     {
00187         if (input[i] != '%')
00188         {
00189             *output++ = input[i];
00190             i++;
00191         }
00192         else
00193         {
00194             *output++ = charFromNibbles(input[i+1], input[i+2]);
00195             i += 3;
00196         }
00197     }
00198 }
00199 
00200 static size_t URL_PrintableCharSize(unsigned char charVal)
00201 {
00202     size_t size;
00203     if (IS_PRINTABLE(charVal))
00204     {
00205         size = 1;
00206     }
00207     else
00208     {
00209         if (charVal < 0x80)
00210         {
00211             size = 3;
00212         }
00213         else
00214         {
00215             size = 6;
00216         }
00217     }
00218     return size;
00219 }
00220 
00221 static STRING_HANDLE encode_url_data(const char* text)
00222 {
00223     STRING_HANDLE result;
00224     size_t lengthOfResult = 0;
00225     char* encodedURL;
00226     unsigned char currentUnsignedChar;
00227     const char* iterator = text;
00228 
00229     /*Codes_SRS_URL_ENCODE_06_003: [If input is a zero length string then URL_Encode will return a zero length string.]*/
00230     do
00231     {
00232         currentUnsignedChar = (unsigned char)(*iterator++);
00233         lengthOfResult += URL_PrintableCharSize(currentUnsignedChar);
00234     } while (currentUnsignedChar != 0);
00235 
00236     if ((encodedURL = (char*)malloc(lengthOfResult)) == NULL)
00237     {
00238         /*Codes_SRS_URL_ENCODE_06_002: [If an error occurs during the encoding of input then URL_Encode will return NULL.]*/
00239         result = NULL;
00240         LogError("URL_Encode:: MALLOC failure on encode.");
00241     }
00242     else
00243     {
00244         size_t currentEncodePosition = 0;
00245         iterator = text;;
00246         do
00247         {
00248             currentUnsignedChar = (unsigned char)(*iterator++);
00249             currentEncodePosition += URL_PrintableChar(currentUnsignedChar, &encodedURL[currentEncodePosition]);
00250         } while (currentUnsignedChar != 0);
00251 
00252         result = STRING_new_with_memory(encodedURL);
00253         if (result == NULL)
00254         {
00255             LogError("URL_Encode:: MALLOC failure on encode.");
00256             free(encodedURL);
00257         }
00258     }
00259     return result;
00260 }
00261 
00262 STRING_HANDLE URL_EncodeString(const char* textEncode)
00263 {
00264     STRING_HANDLE result;
00265     if (textEncode == NULL)
00266     {
00267         result = NULL;
00268     }
00269     else
00270     {
00271         result = encode_url_data(textEncode);
00272     }
00273     return result;
00274 }
00275 
00276 STRING_HANDLE URL_Encode(STRING_HANDLE input)
00277 {
00278     STRING_HANDLE result;
00279     if (input == NULL)
00280     {
00281         /*Codes_SRS_URL_ENCODE_06_001: [If input is NULL then URL_Encode will return NULL.]*/
00282         result = NULL;
00283         LogError("URL_Encode:: NULL input");
00284     }
00285     else
00286     {
00287         result = encode_url_data(STRING_c_str(input));
00288     }
00289     return result;
00290 }
00291 
00292 STRING_HANDLE URL_DecodeString(const char* textDecode)
00293 {
00294     STRING_HANDLE result;
00295     if (textDecode == NULL)
00296     {
00297         result = NULL;
00298     }
00299     else
00300     {
00301         STRING_HANDLE tempString = STRING_construct(textDecode);
00302         if (tempString == NULL)
00303         {
00304             result = NULL;
00305         }
00306         else
00307         {
00308             result = URL_Decode(tempString);
00309             STRING_delete(tempString);
00310         }
00311     }
00312     return result;
00313 }
00314 
00315 STRING_HANDLE URL_Decode(STRING_HANDLE input)
00316 {
00317     STRING_HANDLE result;
00318     if (input == NULL)
00319     {
00320         LogError("URL_Decode:: NULL input");
00321         result = NULL;
00322     }
00323     else
00324     {
00325         size_t decodedStringSize;
00326         char* decodedString;
00327         const char* inputString = STRING_c_str(input);
00328         size_t inputLen = strlen(inputString);
00329         if ((decodedStringSize = calculateDecodedStringSize(inputString, inputLen)) == 0)
00330         {
00331             LogError("URL_Decode:: Invalid input string");
00332             result = NULL;
00333         }
00334         else if ((decodedString = (char*)malloc(decodedStringSize)) == NULL)
00335         {
00336             LogError("URL_Decode:: MALLOC failure on decode.");
00337             result = NULL;
00338         }
00339         else
00340         {
00341             createDecodedString(inputString, inputLen, decodedString);
00342             result = STRING_new_with_memory(decodedString);
00343             if (result == NULL)
00344             {
00345                 LogError("URL_Decode:: MALLOC failure on decode");
00346                 free(decodedString);
00347             }
00348         }
00349     }
00350     return result;
00351 }