]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/common/src/aws_iot_doc_parser.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / aws / common / src / aws_iot_doc_parser.c
1 /*\r
2  * AWS IoT Common V1.0.0\r
3  * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  */\r
22 \r
23 /**\r
24  * @file aws_iot_doc_parser.c\r
25  * @brief Implements the functions in aws_iot_doc_parser.h\r
26  */\r
27 \r
28 /* The config header is always included first. */\r
29 #include "iot_config.h"\r
30 \r
31 /* Standard includes. */\r
32 #include <string.h>\r
33 \r
34 /* JSON utilities include. */\r
35 #include "aws_iot_doc_parser.h"\r
36 \r
37 #define IS_QUOTE( str, idx ) \\r
38     ( ( str )[ ( idx ) ] == '"' && ( ( idx ) == 0 || ( str )[ ( idx ) - 1 ] != '\\' ) )\r
39 #define IS_WHITESPACE( str, idx ) \\r
40     ( ( str )[ ( idx ) ] == ' ' || ( str )[ ( idx ) ] == '\n' || ( str )[ ( idx ) ] == '\r' || ( str )[ ( idx ) ] == '\t' )\r
41 \r
42 /*-----------------------------------------------------------*/\r
43 \r
44 bool AwsIotDocParser_FindValue( const char * pAwsIotJsonDocument,\r
45                                 size_t awsIotJsonDocumentLength,\r
46                                 const char * pAwsIotJsonKey,\r
47                                 size_t awsIotJsonKeyLength,\r
48                                 const char ** pAwsIotJsonValue,\r
49                                 size_t * pAwsIotJsonValueLength )\r
50 {\r
51     size_t i = 0;\r
52     size_t jsonValueLength = 0;\r
53     char openCharacter = '\0', closeCharacter = '\0';\r
54     int nestingLevel = 0;\r
55     bool isWithinQuotes = false;\r
56 \r
57     /* Validate all the arguments.*/\r
58     if( ( pAwsIotJsonDocument == NULL ) || ( pAwsIotJsonKey == NULL ) ||\r
59         ( awsIotJsonDocumentLength == 0 ) || ( awsIotJsonKeyLength == 0 ) )\r
60     {\r
61         return false;\r
62     }\r
63 \r
64     /* Ensure the JSON document is long enough to contain the key/value pair. At\r
65      * the very least, a JSON key/value pair must contain the key and the 6\r
66      * characters {":""} */\r
67     if( awsIotJsonDocumentLength < awsIotJsonKeyLength + 6 )\r
68     {\r
69         return false;\r
70     }\r
71 \r
72     /* Search the characters in the JSON document for the key. The end of the JSON\r
73      * document does not have to be searched once too few characters remain to hold a\r
74      * value. */\r
75     while( i < awsIotJsonDocumentLength - awsIotJsonKeyLength - 3 )\r
76     {\r
77         /* If the first character in the key is found and there's an unescaped double\r
78          * quote after the key length, do a string compare for the key. */\r
79         if( ( IS_QUOTE( pAwsIotJsonDocument, i ) ) &&\r
80             ( IS_QUOTE( pAwsIotJsonDocument, i + 1 + awsIotJsonKeyLength ) ) &&\r
81             ( pAwsIotJsonDocument[ i + 1 ] == pAwsIotJsonKey[ 0 ] ) &&\r
82             ( strncmp( pAwsIotJsonDocument + i + 1,\r
83                        pAwsIotJsonKey,\r
84                        awsIotJsonKeyLength ) == 0 ) )\r
85         {\r
86             /* Key found; this is a potential match. */\r
87 \r
88             /* Skip the characters in the JSON key and closing double quote. */\r
89             /* While loop guarantees that i < awsIotJsonDocumentLength - 1 */\r
90             i += awsIotJsonKeyLength + 2;\r
91 \r
92             /* Skip all whitespace characters between the closing " and the : */\r
93             while( IS_WHITESPACE( pAwsIotJsonDocument, i ) )\r
94             {\r
95                 i++;\r
96 \r
97                 /* If the end of the document is reached, this isn't a match. */\r
98                 if( i >= awsIotJsonDocumentLength )\r
99                 {\r
100                     return false;\r
101                 }\r
102             }\r
103 \r
104             /* The character immediately following a key (and any whitespace) must be a :\r
105              * If it's another character, then this string is a JSON value; skip it. */\r
106             if( pAwsIotJsonDocument[ i ] != ':' )\r
107             {\r
108                 continue;\r
109             }\r
110             else\r
111             {\r
112                 /* Skip the : */\r
113                 i++;\r
114             }\r
115 \r
116             /* If the end of the document is reached, this isn't a match. */\r
117             if( i >= awsIotJsonDocumentLength )\r
118             {\r
119                 return false;\r
120             }\r
121 \r
122             /* Skip all whitespace characters between : and the first character in the value. */\r
123             while( IS_WHITESPACE( pAwsIotJsonDocument, i ) )\r
124             {\r
125                 i++;\r
126 \r
127                 /* If the end of the document is reached, this isn't a match. */\r
128                 if( i >= awsIotJsonDocumentLength )\r
129                 {\r
130                     return false;\r
131                 }\r
132             }\r
133 \r
134             /* Value found. Set the output parameter. */\r
135             if( pAwsIotJsonValue != NULL )\r
136             {\r
137                 *pAwsIotJsonValue = pAwsIotJsonDocument + i;\r
138             }\r
139 \r
140             /* Calculate the value's length. */\r
141             switch( pAwsIotJsonDocument[ i ] )\r
142             {\r
143                 /* Calculate length of a JSON string. */\r
144                 case '\"':\r
145                     /* Include the length of the opening and closing double quotes. */\r
146                     jsonValueLength = 2;\r
147 \r
148                     /* Skip the opening double quote. */\r
149                     i++;\r
150 \r
151                     /* If the end of the document is reached, this isn't a match. */\r
152                     if( i >= awsIotJsonDocumentLength )\r
153                     {\r
154                         return false;\r
155                     }\r
156 \r
157                     /* Add the length of all characters in the JSON string. */\r
158                     while( pAwsIotJsonDocument[ i ] != '\"' )\r
159                     {\r
160                         /* Ignore escaped double quotes. */\r
161                         if( ( pAwsIotJsonDocument[ i ] == '\\' ) &&\r
162                             ( i + 1 < awsIotJsonDocumentLength ) &&\r
163                             ( pAwsIotJsonDocument[ i + 1 ] == '\"' ) )\r
164                         {\r
165                             /* Skip the characters \" */\r
166                             i += 2;\r
167                             jsonValueLength += 2;\r
168                         }\r
169                         else\r
170                         {\r
171                             /* Add the length of a single character. */\r
172                             i++;\r
173                             jsonValueLength++;\r
174                         }\r
175 \r
176                         /* If the end of the document is reached, this isn't a match. */\r
177                         if( i >= awsIotJsonDocumentLength )\r
178                         {\r
179                             return false;\r
180                         }\r
181                     }\r
182 \r
183                     break;\r
184 \r
185                 /* Set the matching opening and closing characters of a JSON object or array.\r
186                  * The length calculation is performed below. */\r
187                 case '{':\r
188                     openCharacter = '{';\r
189                     closeCharacter = '}';\r
190                     break;\r
191 \r
192                 case '[':\r
193                     openCharacter = '[';\r
194                     closeCharacter = ']';\r
195                     break;\r
196 \r
197                 /* Calculate the length of a JSON primitive. */\r
198                 default:\r
199 \r
200                     /* Skip the characters in the JSON value. The JSON value ends with a , or } */\r
201                     while( pAwsIotJsonDocument[ i ] != ',' &&\r
202                            pAwsIotJsonDocument[ i ] != '}' )\r
203                     {\r
204                         /* Any whitespace before a , or } means the JSON document is invalid. */\r
205                         if( IS_WHITESPACE( pAwsIotJsonDocument, i ) )\r
206                         {\r
207                             return false;\r
208                         }\r
209 \r
210                         i++;\r
211                         jsonValueLength++;\r
212 \r
213                         /* If the end of the document is reached, this isn't a match. */\r
214                         if( i >= awsIotJsonDocumentLength )\r
215                         {\r
216                             return false;\r
217                         }\r
218                     }\r
219 \r
220                     break;\r
221             }\r
222 \r
223             /* Calculate the length of a JSON object or array. */\r
224             if( ( openCharacter != '\0' ) && ( closeCharacter != '\0' ) )\r
225             {\r
226                 /* Include the length of the opening and closing characters. */\r
227                 jsonValueLength = 2;\r
228 \r
229                 /* Skip the opening character. */\r
230                 i++;\r
231 \r
232                 /* If the end of the document is reached, this isn't a match. */\r
233                 if( i >= awsIotJsonDocumentLength )\r
234                 {\r
235                     return false;\r
236                 }\r
237 \r
238                 /* Add the length of all characters in the JSON object or array. This\r
239                  * includes the length of nested objects. */\r
240                 while( pAwsIotJsonDocument[ i ] != closeCharacter ||\r
241                        ( pAwsIotJsonDocument[ i ] == closeCharacter && ( nestingLevel != 0 || isWithinQuotes ) ) )\r
242                 {\r
243                     /* Check if its a quote so as to avoid considering the\r
244                      * nested levels for opening and closing characters within\r
245                      * quotes.\r
246                      */\r
247                     if( IS_QUOTE( pAwsIotJsonDocument, i ) )\r
248                     {\r
249                         /*Toggle the flag*/\r
250                         isWithinQuotes = !isWithinQuotes;\r
251                     }\r
252 \r
253                     /* Calculate the nesting levels only if not in quotes. */\r
254                     if( !isWithinQuotes )\r
255                     {\r
256                         /* An opening character starts a nested object. */\r
257                         if( pAwsIotJsonDocument[ i ] == openCharacter )\r
258                         {\r
259                             nestingLevel++;\r
260                         }\r
261                         /* A closing character ends a nested object. */\r
262                         else if( pAwsIotJsonDocument[ i ] == closeCharacter )\r
263                         {\r
264                             nestingLevel--;\r
265                         }\r
266                     }\r
267 \r
268                     i++;\r
269                     jsonValueLength++;\r
270 \r
271                     /* If the end of the document is reached, this isn't a match. */\r
272                     if( i >= awsIotJsonDocumentLength )\r
273                     {\r
274                         return false;\r
275                     }\r
276                 }\r
277             }\r
278 \r
279             /* JSON value length calculated; set the output parameter. */\r
280             if( pAwsIotJsonValueLength != NULL )\r
281             {\r
282                 *pAwsIotJsonValueLength = jsonValueLength;\r
283             }\r
284 \r
285             return true;\r
286         }\r
287 \r
288         i++;\r
289     }\r
290 \r
291     return false;\r
292 }\r
293 \r
294 /*-----------------------------------------------------------*/\r