]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/jobs/src/aws_iot_jobs_serialize.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / aws / jobs / src / aws_iot_jobs_serialize.c
1 /*\r
2  * AWS IoT Jobs V1.0.0\r
3  * Copyright (C) 2019 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_jobs_serialize.c\r
25  * @brief Implements functions that generate and parse Jobs JSON documents.\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 <stdio.h>\r
33 #include <string.h>\r
34 \r
35 /* Jobs internal include. */\r
36 #include "private/aws_iot_jobs_internal.h"\r
37 \r
38 /* Error handling include. */\r
39 #include "iot_error.h"\r
40 \r
41 /* JSON utilities include. */\r
42 #include "aws_iot_doc_parser.h"\r
43 \r
44 /**\r
45  * @brief Minimum length of a Jobs request.\r
46  *\r
47  * At the very least, the request will contain: {"clientToken":""}\r
48  */\r
49 #define MINIMUM_REQUEST_LENGTH                    ( AWS_IOT_CLIENT_TOKEN_KEY_LENGTH + 7 )\r
50 \r
51 /**\r
52  * @brief The length of client tokens generated by this library.\r
53  */\r
54 #define CLIENT_TOKEN_AUTOGENERATE_LENGTH          ( 8 )\r
55 \r
56 /**\r
57  * @brief JSON key representing Jobs status.\r
58  */\r
59 #define STATUS_KEY                                "status"\r
60 \r
61 /**\r
62  * @brief Length of #STATUS_KEY.\r
63  */\r
64 #define STATUS_KEY_LENGTH                         ( sizeof( STATUS_KEY ) - 1 )\r
65 \r
66 /**\r
67  * @brief JSON key representing Jobs status details.\r
68  */\r
69 #define STATUS_DETAILS_KEY                        "statusDetails"\r
70 \r
71 /**\r
72  * @brief Length of #STATUS_DETAILS_KEY.\r
73  */\r
74 #define STATUS_DETAILS_KEY_LENGTH                 ( sizeof( STATUS_DETAILS_KEY ) - 1 )\r
75 \r
76 /**\r
77  * @brief JSON key representing Jobs expected version.\r
78  */\r
79 #define EXPECTED_VERSION_KEY                      "expectedVersion"\r
80 \r
81 /**\r
82  * @brief Length of #EXPECTED_VERSION_KEY.\r
83  */\r
84 #define EXPECTED_VERSION_KEY_LENGTH               ( sizeof( EXPECTED_VERSION_KEY ) - 1 )\r
85 \r
86 /**\r
87  * @brief Maximum length of the expected version when represented as a string.\r
88  *\r
89  * The expected version is a 32-bit unsigned integer. This can be represented in\r
90  * 10 digits plus a NULL-terminator.\r
91  */\r
92 #define EXPECTED_VERSION_STRING_LENGTH            ( 11 )\r
93 \r
94 /**\r
95  * @brief JSON key representing Jobs step timeout.\r
96  */\r
97 #define STEP_TIMEOUT_KEY                          "stepTimeoutInMinutes"\r
98 \r
99 /**\r
100  * @brief Length of #STEP_TIMEOUT_KEY.\r
101  */\r
102 #define STEP_TIMEOUT_KEY_LENGTH                   ( sizeof( STEP_TIMEOUT_KEY ) - 1 )\r
103 \r
104 /**\r
105  * @brief Maximum length of the step timeout when represented as a string.\r
106  *\r
107  * The step timeout is in the range of [-1,10080]. This can be represented as\r
108  * 5 digits plus a NULL-terminator.\r
109  */\r
110 #define STEP_TIMEOUT_STRING_LENGTH                ( 6 )\r
111 \r
112 /**\r
113  * @brief JSON key representing the "include Job document" flag.\r
114  */\r
115 #define INCLUDE_JOB_DOCUMENT_KEY                  "includeJobDocument"\r
116 \r
117 /**\r
118  * @brief JSON key representing the "include Job Execution state" flag.\r
119  */\r
120 #define INCLUDE_JOB_EXECUTION_STATE_KEY           "includeJobExecutionState"\r
121 \r
122 /**\r
123  * @brief Length of #INCLUDE_JOB_EXECUTION_STATE_KEY.\r
124  */\r
125 #define INCLUDE_JOB_EXECUTION_STATE_KEY_LENGTH    ( sizeof( INCLUDE_JOB_EXECUTION_STATE_KEY ) - 1 )\r
126 \r
127 /**\r
128  * @brief Length of #INCLUDE_JOB_DOCUMENT_KEY.\r
129  */\r
130 #define INCLUDE_JOB_DOCUMENT_KEY_LENGTH           ( sizeof( INCLUDE_JOB_DOCUMENT_KEY ) - 1 )\r
131 \r
132 /**\r
133  * @brief JSON key representing the Jobs execution number.\r
134  */\r
135 #define EXECUTION_NUMBER_KEY                      "executionNumber"\r
136 \r
137 /**\r
138  * @brief Length of #EXECUTION_NUMBER_KEY.\r
139  */\r
140 #define EXECUTION_NUMBER_KEY_LENGTH               ( sizeof( EXECUTION_NUMBER_KEY ) - 1 )\r
141 \r
142 /**\r
143  * @brief Maximum length of the execution number when represented as a string.\r
144  *\r
145  * The execution number is a 32-bit integer. This can be represented in 10 digits,\r
146  * plus 1 for a possible negative sign, plus a NULL-terminator.\r
147  */\r
148 #define EXECUTION_NUMBER_STRING_LENGTH            ( 12 )\r
149 \r
150 /**\r
151  * @brief JSON key representing Jobs error code in error responses.\r
152  */\r
153 #define CODE_KEY                                  "code"\r
154 \r
155 /**\r
156  * @brief Length of #CODE_KEY.\r
157  */\r
158 #define CODE_KEY_LENGTH                           ( sizeof( CODE_KEY ) - 1 )\r
159 \r
160 /**\r
161  * @brief Append a string to a buffer.\r
162  *\r
163  * Also updates `copyOffset` with `stringLength`.\r
164  *\r
165  * @param[in] pBuffer Start of a buffer.\r
166  * @param[in] copyOffset Offset in `pBuffer` where `pString` will be placed.\r
167  * @param[in] pString The string to append.\r
168  * @param[in] stringLength Length of `pString`.\r
169  */\r
170 #define APPEND_STRING( pBuffer, copyOffset, pString, stringLength ) \\r
171     ( void ) memcpy( pBuffer + copyOffset, pString, stringLength ); \\r
172     copyOffset += ( size_t ) stringLength;\r
173 \r
174 /*-----------------------------------------------------------*/\r
175 \r
176 /**\r
177  * @brief Place a JSON boolean flag in the given buffer.\r
178  *\r
179  * @param[in] pBuffer The buffer where the flag is placed.\r
180  * @param[in] copyOffset Offset in `pBuffer` where the flag is placed.\r
181  * @param[in] pFlagName Either #INCLUDE_JOB_DOCUMENT_KEY or #INCLUDE_JOB_EXECUTION_STATE_KEY.\r
182  * @param[in] flagNameLength Either #INCLUDE_JOB_EXECUTION_STATE_KEY_LENGTH or\r
183  * #INCLUDE_JOB_EXECUTION_STATE_KEY_LENGTH\r
184  * @param[in] value Either `true` or `false`.\r
185  *\r
186  * @warning This function does not check the length of `pBuffer`! Any provided\r
187  * buffer must be large enough to accommodate the flag and value.\r
188  *\r
189  * @return A value of `copyOffset` after the flag.\r
190  */\r
191 static size_t _appendFlag( char * pBuffer,\r
192                            size_t copyOffset,\r
193                            const char * pFlagName,\r
194                            size_t flagNameLength,\r
195                            bool value );\r
196 \r
197 /**\r
198  * @brief Place Job status details in the given buffer.\r
199  *\r
200  * @param[in] pBuffer The buffer where the status details are placed.\r
201  * @param[in] copyOffset Offset in `pBuffer` where the status details are placed.\r
202  * @param[in] pStatusDetails The status details to place in the buffer.\r
203  * @param[in] statusDetailsLength Length of `pStatusDetails`.\r
204  *\r
205  * @warning This function does not check the length of `pBuffer`! Any provided\r
206  * buffer must be large enough to accommodate the status details.\r
207  *\r
208  * @return A value of `copyOffset` after the status details.\r
209  */\r
210 static size_t _appendStatusDetails( char * pBuffer,\r
211                                     size_t copyOffset,\r
212                                     const char * pStatusDetails,\r
213                                     size_t statusDetailsLength );\r
214 \r
215 /**\r
216  * @brief Place Job execution number in the given buffer.\r
217  *\r
218  * @param[in] pBuffer The buffer where the execution number is placed.\r
219  * @param[in] copyOffset Offset in `pBuffer` where the execution number is placed.\r
220  * @param[in] pExecutionNumber The execution number to place in the buffer.\r
221  * @param[in] executionNumberLength Length of `pExecutionNumber`.\r
222  *\r
223  * @warning This function does not check the length of `pBuffer`! Any provided\r
224  * buffer must be large enough to accommodate the execution number.\r
225  *\r
226  * @return A value of `copyOffset` after the execution number.\r
227  */\r
228 static size_t _appendExecutionNumber( char * pBuffer,\r
229                                       size_t copyOffset,\r
230                                       const char * pExecutionNumber,\r
231                                       size_t executionNumberLength );\r
232 \r
233 /**\r
234  * @brief Place Job step timeout in the given buffer.\r
235  *\r
236  * @param[in] pBuffer The buffer where the step timeout is placed.\r
237  * @param[in] copyOffset Offset in `pBuffer` where the step timeout is placed.\r
238  * @param[in] pStepTimeout The step timeout to place in the buffer.\r
239  * @param[in] stepTimeoutLength Length of `pStepTimeout`.\r
240  *\r
241  * @warning This function does not check the length of `pBuffer`! Any provided\r
242  * buffer must be large enough to accommodate the step timeout.\r
243  *\r
244  * @return A value of `copyOffset` after the step timeout.\r
245  */\r
246 static size_t _appendStepTimeout( char * pBuffer,\r
247                                   size_t copyOffset,\r
248                                   const char * pStepTimeout,\r
249                                   size_t stepTimeoutLength );\r
250 \r
251 /**\r
252  * @brief Place a client token in the given buffer.\r
253  *\r
254  * @param[in] pBuffer The buffer where the client token is placed.\r
255  * @param[in] copyOffset Offset in `pBuffer` where client token is placed.\r
256  * @param[in] pRequestInfo Contains information on a client token to place.\r
257  * @param[out] pOperation Location and length of client token are written here.\r
258  *\r
259  * @warning This function does not check the length of `pBuffer`! Any provided\r
260  * buffer must be large enough to accommodate #CLIENT_TOKEN_AUTOGENERATE_LENGTH\r
261  * characters.\r
262  *\r
263  * @return A value of `copyOffset` after the client token.\r
264  */\r
265 static size_t _appendClientToken( char * pBuffer,\r
266                                   size_t copyOffset,\r
267                                   const AwsIotJobsRequestInfo_t * pRequestInfo,\r
268                                   _jobsOperation_t * pOperation );\r
269 \r
270 /**\r
271  * @brief Generates a request JSON for a GET PENDING operation.\r
272  *\r
273  * @param[in] pRequestInfo Common Jobs request parameters.\r
274  * @param[in] pOperation Operation associated with the Jobs request.\r
275  *\r
276  * @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY\r
277  */\r
278 static AwsIotJobsError_t _generateGetPendingRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
279                                                      _jobsOperation_t * pOperation );\r
280 \r
281 /**\r
282  * @brief Generates a request JSON for a START NEXT operation.\r
283  *\r
284  * @param[in] pRequestInfo Common Jobs request parameters.\r
285  * @param[in] pUpdateInfo Jobs update parameters.\r
286  * @param[in] pOperation Operation associated with the Jobs request.\r
287  *\r
288  * @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY\r
289  */\r
290 static AwsIotJobsError_t _generateStartNextRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
291                                                     const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
292                                                     _jobsOperation_t * pOperation );\r
293 \r
294 /**\r
295  * @brief Generates a request JSON for a DESCRIBE operation.\r
296  *\r
297  * @param[in] pRequestInfo Common jobs request parameters.\r
298  * @param[in] executionNumber Job execution number to include in request.\r
299  * @param[in] includeJobDocument Whether the response should include the Job document.\r
300  * @param[in] pOperation Operation associated with the Jobs request.\r
301  *\r
302  * @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY.\r
303  */\r
304 static AwsIotJobsError_t _generateDescribeRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
305                                                    int32_t executionNumber,\r
306                                                    bool includeJobDocument,\r
307                                                    _jobsOperation_t * pOperation );\r
308 \r
309 /**\r
310  * @brief Generates a request JSON for an UPDATE operation.\r
311  *\r
312  * @param[in] pRequestInfo Common Jobs request parameters.\r
313  * @param[in] pUpdateInfo Jobs update parameters.\r
314  * @param[in] pOperation Operation associated with the Jobs request.\r
315  */\r
316 static AwsIotJobsError_t _generateUpdateRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
317                                                  const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
318                                                  _jobsOperation_t * pOperation );\r
319 \r
320 /**\r
321  * @brief Parse an error from a Jobs error document.\r
322  *\r
323  * @param[in] pErrorDocument Jobs error document.\r
324  * @param[in] errorDocumentLength Length of `pErrorDocument`.\r
325  *\r
326  * @return A Jobs error code between #AWS_IOT_JOBS_INVALID_TOPIC and\r
327  * #AWS_IOT_JOBS_TERMINAL_STATE.\r
328  */\r
329 static AwsIotJobsError_t _parseErrorDocument( const char * pErrorDocument,\r
330                                               size_t errorDocumentLength );\r
331 \r
332 /*-----------------------------------------------------------*/\r
333 \r
334 static size_t _appendFlag( char * pBuffer,\r
335                            size_t copyOffset,\r
336                            const char * pFlagName,\r
337                            size_t flagNameLength,\r
338                            bool value )\r
339 {\r
340     if( value == true )\r
341     {\r
342         APPEND_STRING( pBuffer,\r
343                        copyOffset,\r
344                        pFlagName,\r
345                        flagNameLength );\r
346         APPEND_STRING( pBuffer, copyOffset, "\":true,\"", 8 );\r
347     }\r
348     else\r
349     {\r
350         APPEND_STRING( pBuffer,\r
351                        copyOffset,\r
352                        pFlagName,\r
353                        flagNameLength );\r
354         APPEND_STRING( pBuffer, copyOffset, "\":false,\"", 9 );\r
355     }\r
356 \r
357     return copyOffset;\r
358 }\r
359 \r
360 /*-----------------------------------------------------------*/\r
361 \r
362 static size_t _appendStatusDetails( char * pBuffer,\r
363                                     size_t copyOffset,\r
364                                     const char * pStatusDetails,\r
365                                     size_t statusDetailsLength )\r
366 {\r
367     APPEND_STRING( pBuffer, copyOffset, STATUS_DETAILS_KEY, STATUS_DETAILS_KEY_LENGTH );\r
368     APPEND_STRING( pBuffer, copyOffset, "\":", 2 );\r
369     APPEND_STRING( pBuffer,\r
370                    copyOffset,\r
371                    pStatusDetails,\r
372                    statusDetailsLength );\r
373     APPEND_STRING( pBuffer, copyOffset, ",\"", 2 );\r
374 \r
375     return copyOffset;\r
376 }\r
377 \r
378 /*-----------------------------------------------------------*/\r
379 \r
380 static size_t _appendExecutionNumber( char * pBuffer,\r
381                                       size_t copyOffset,\r
382                                       const char * pExecutionNumber,\r
383                                       size_t executionNumberLength )\r
384 {\r
385     APPEND_STRING( pBuffer,\r
386                    copyOffset,\r
387                    EXECUTION_NUMBER_KEY,\r
388                    EXECUTION_NUMBER_KEY_LENGTH );\r
389     APPEND_STRING( pBuffer,\r
390                    copyOffset,\r
391                    "\":",\r
392                    2 );\r
393     APPEND_STRING( pBuffer,\r
394                    copyOffset,\r
395                    pExecutionNumber,\r
396                    executionNumberLength );\r
397     APPEND_STRING( pBuffer, copyOffset, ",\"", 2 );\r
398 \r
399     return copyOffset;\r
400 }\r
401 \r
402 /*-----------------------------------------------------------*/\r
403 \r
404 static size_t _appendStepTimeout( char * pBuffer,\r
405                                   size_t copyOffset,\r
406                                   const char * pStepTimeout,\r
407                                   size_t stepTimeoutLength )\r
408 {\r
409     APPEND_STRING( pBuffer,\r
410                    copyOffset,\r
411                    STEP_TIMEOUT_KEY,\r
412                    STEP_TIMEOUT_KEY_LENGTH );\r
413     APPEND_STRING( pBuffer, copyOffset, "\":", 2 );\r
414     APPEND_STRING( pBuffer, copyOffset, pStepTimeout, stepTimeoutLength );\r
415     APPEND_STRING( pBuffer, copyOffset, ",\"", 2 );\r
416 \r
417     return copyOffset;\r
418 }\r
419 \r
420 /*-----------------------------------------------------------*/\r
421 \r
422 static size_t _appendClientToken( char * pBuffer,\r
423                                   size_t copyOffset,\r
424                                   const AwsIotJobsRequestInfo_t * pRequestInfo,\r
425                                   _jobsOperation_t * pOperation )\r
426 {\r
427     int clientTokenLength = 0;\r
428     uint32_t clientToken = 0;\r
429 \r
430     /* Place the client token key in the buffer. */\r
431     APPEND_STRING( pBuffer,\r
432                    copyOffset,\r
433                    AWS_IOT_CLIENT_TOKEN_KEY,\r
434                    AWS_IOT_CLIENT_TOKEN_KEY_LENGTH );\r
435     APPEND_STRING( pBuffer, copyOffset, "\":\"", 3 );\r
436 \r
437     /* Set the pointer to the client token. */\r
438     pOperation->pClientToken = pBuffer + copyOffset - 1;\r
439 \r
440     if( pRequestInfo->pClientToken == AWS_IOT_JOBS_CLIENT_TOKEN_AUTOGENERATE )\r
441     {\r
442         /* Take the address of the given buffer, truncated to 8 characters. This\r
443          * provides a client token that is very likely to be unique while in use. */\r
444         clientToken = ( uint32_t ) ( ( uint64_t ) pBuffer % 100000000ULL );\r
445 \r
446         clientTokenLength = snprintf( pBuffer + copyOffset,\r
447                                       CLIENT_TOKEN_AUTOGENERATE_LENGTH + 1,\r
448                                       "%08u", clientToken );\r
449         AwsIotJobs_Assert( clientTokenLength == CLIENT_TOKEN_AUTOGENERATE_LENGTH );\r
450 \r
451         copyOffset += ( size_t ) clientTokenLength;\r
452         pOperation->clientTokenLength = CLIENT_TOKEN_AUTOGENERATE_LENGTH + 2;\r
453     }\r
454     else\r
455     {\r
456         APPEND_STRING( pBuffer,\r
457                        copyOffset,\r
458                        pRequestInfo->pClientToken,\r
459                        pRequestInfo->clientTokenLength );\r
460 \r
461         pOperation->clientTokenLength = pRequestInfo->clientTokenLength + 2;\r
462     }\r
463 \r
464     return copyOffset;\r
465 }\r
466 \r
467 /*-----------------------------------------------------------*/\r
468 \r
469 static AwsIotJobsError_t _generateGetPendingRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
470                                                      _jobsOperation_t * pOperation )\r
471 {\r
472     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );\r
473     char * pJobsRequest = NULL;\r
474     size_t copyOffset = 0;\r
475     size_t requestLength = MINIMUM_REQUEST_LENGTH;\r
476 \r
477     /* Add the length of the client token. */\r
478     if( pRequestInfo->pClientToken != AWS_IOT_JOBS_CLIENT_TOKEN_AUTOGENERATE )\r
479     {\r
480         AwsIotJobs_Assert( pRequestInfo->clientTokenLength > 0 );\r
481 \r
482         requestLength += pRequestInfo->clientTokenLength;\r
483     }\r
484     else\r
485     {\r
486         requestLength += CLIENT_TOKEN_AUTOGENERATE_LENGTH;\r
487     }\r
488 \r
489     /* Allocate memory for the request JSON. */\r
490     pJobsRequest = AwsIotJobs_MallocString( requestLength );\r
491 \r
492     if( pJobsRequest == NULL )\r
493     {\r
494         IotLogError( "No memory for Jobs GET PENDING request." );\r
495 \r
496         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
497     }\r
498 \r
499     /* Clear the request JSON. */\r
500     ( void ) memset( pJobsRequest, 0x00, requestLength );\r
501 \r
502     /* Construct the request JSON, which consists of just a clientToken key. */\r
503     APPEND_STRING( pJobsRequest, copyOffset, "{\"", 2 );\r
504     copyOffset = _appendClientToken( pJobsRequest, copyOffset, pRequestInfo, pOperation );\r
505     APPEND_STRING( pJobsRequest, copyOffset, "\"}", 2 );\r
506 \r
507     /* Set the output parameters. */\r
508     pOperation->pJobsRequest = pJobsRequest;\r
509     pOperation->jobsRequestLength = requestLength;\r
510 \r
511     /* Ensure offsets are valid. */\r
512     AwsIotJobs_Assert( copyOffset == requestLength );\r
513     AwsIotJobs_Assert( pOperation->pClientToken > pOperation->pJobsRequest );\r
514     AwsIotJobs_Assert( pOperation->pClientToken <\r
515                        pOperation->pJobsRequest + pOperation->jobsRequestLength );\r
516 \r
517     IotLogDebug( "Jobs GET PENDING request: %.*s",\r
518                  pOperation->jobsRequestLength,\r
519                  pOperation->pJobsRequest );\r
520 \r
521     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
522 }\r
523 \r
524 /*-----------------------------------------------------------*/\r
525 \r
526 static AwsIotJobsError_t _generateStartNextRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
527                                                     const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
528                                                     _jobsOperation_t * pOperation )\r
529 {\r
530     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );\r
531     char * pJobsRequest = NULL;\r
532     size_t copyOffset = 0;\r
533     size_t requestLength = MINIMUM_REQUEST_LENGTH;\r
534     char pStepTimeout[ STEP_TIMEOUT_STRING_LENGTH ] = { 0 };\r
535     int stepTimeoutLength = 0;\r
536 \r
537     /* Add the length of status details if provided. */\r
538     if( pUpdateInfo->pStatusDetails != AWS_IOT_JOBS_NO_STATUS_DETAILS )\r
539     {\r
540         /* Add 4 for the 2 quotes, colon, and comma. */\r
541         requestLength += STATUS_DETAILS_KEY_LENGTH + 4;\r
542         requestLength += pUpdateInfo->statusDetailsLength;\r
543     }\r
544 \r
545     if( pUpdateInfo->stepTimeoutInMinutes != AWS_IOT_JOBS_NO_TIMEOUT )\r
546     {\r
547         /* Calculate the length of the step timeout. Add 4 for the 2 quotes, colon, and comma. */\r
548         requestLength += STEP_TIMEOUT_KEY_LENGTH + 4;\r
549 \r
550         if( pUpdateInfo->stepTimeoutInMinutes == AWS_IOT_JOBS_CANCEL_TIMEOUT )\r
551         {\r
552             /* Step timeout will be set to -1. */\r
553             pStepTimeout[ 0 ] = '-';\r
554             pStepTimeout[ 1 ] = '1';\r
555             stepTimeoutLength = 2;\r
556         }\r
557         else\r
558         {\r
559             /* Convert the step timeout to a string. */\r
560             stepTimeoutLength = snprintf( pStepTimeout,\r
561                                           STEP_TIMEOUT_STRING_LENGTH,\r
562                                           "%d",\r
563                                           pUpdateInfo->stepTimeoutInMinutes );\r
564             AwsIotJobs_Assert( stepTimeoutLength > 0 );\r
565             AwsIotJobs_Assert( stepTimeoutLength < STEP_TIMEOUT_STRING_LENGTH );\r
566         }\r
567 \r
568         requestLength += ( size_t ) stepTimeoutLength;\r
569     }\r
570 \r
571     /* Add the length of the client token. */\r
572     if( pRequestInfo->pClientToken != AWS_IOT_JOBS_CLIENT_TOKEN_AUTOGENERATE )\r
573     {\r
574         AwsIotJobs_Assert( pRequestInfo->clientTokenLength > 0 );\r
575 \r
576         requestLength += pRequestInfo->clientTokenLength;\r
577     }\r
578     else\r
579     {\r
580         requestLength += CLIENT_TOKEN_AUTOGENERATE_LENGTH;\r
581     }\r
582 \r
583     /* Allocate memory for the request JSON. */\r
584     pJobsRequest = AwsIotJobs_MallocString( requestLength );\r
585 \r
586     if( pJobsRequest == NULL )\r
587     {\r
588         IotLogError( "No memory for Jobs START NEXT request." );\r
589 \r
590         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
591     }\r
592 \r
593     /* Clear the request JSON. */\r
594     ( void ) memset( pJobsRequest, 0x00, requestLength );\r
595 \r
596     /* Construct the request JSON. */\r
597     APPEND_STRING( pJobsRequest, copyOffset, "{\"", 2 );\r
598 \r
599     /* Add status details if present. */\r
600     if( pUpdateInfo->pStatusDetails != AWS_IOT_JOBS_NO_STATUS_DETAILS )\r
601     {\r
602         copyOffset = _appendStatusDetails( pJobsRequest,\r
603                                            copyOffset,\r
604                                            pUpdateInfo->pStatusDetails,\r
605                                            pUpdateInfo->statusDetailsLength );\r
606     }\r
607 \r
608     /* Add step timeout if present. */\r
609     if( pUpdateInfo->stepTimeoutInMinutes != AWS_IOT_JOBS_NO_TIMEOUT )\r
610     {\r
611         copyOffset = _appendStepTimeout( pJobsRequest,\r
612                                          copyOffset,\r
613                                          pStepTimeout,\r
614                                          stepTimeoutLength );\r
615     }\r
616 \r
617     /* Add client token. */\r
618     copyOffset = _appendClientToken( pJobsRequest, copyOffset, pRequestInfo, pOperation );\r
619 \r
620     APPEND_STRING( pJobsRequest, copyOffset, "\"}", 2 );\r
621 \r
622     /* Set the output parameters. */\r
623     pOperation->pJobsRequest = pJobsRequest;\r
624     pOperation->jobsRequestLength = requestLength;\r
625 \r
626     /* Ensure offsets are valid. */\r
627     AwsIotJobs_Assert( copyOffset == requestLength );\r
628     AwsIotJobs_Assert( pOperation->pClientToken > pOperation->pJobsRequest );\r
629     AwsIotJobs_Assert( pOperation->pClientToken <\r
630                        pOperation->pJobsRequest + pOperation->jobsRequestLength );\r
631 \r
632     IotLogDebug( "Jobs START NEXT request: %.*s",\r
633                  pOperation->jobsRequestLength,\r
634                  pOperation->pJobsRequest );\r
635 \r
636     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
637 }\r
638 \r
639 /*-----------------------------------------------------------*/\r
640 \r
641 static AwsIotJobsError_t _generateDescribeRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
642                                                    int32_t executionNumber,\r
643                                                    bool includeJobDocument,\r
644                                                    _jobsOperation_t * pOperation )\r
645 {\r
646     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );\r
647     char * pJobsRequest = NULL;\r
648     size_t copyOffset = 0;\r
649     size_t requestLength = MINIMUM_REQUEST_LENGTH;\r
650     char pExecutionNumber[ EXECUTION_NUMBER_STRING_LENGTH ] = { 0 };\r
651     int executionNumberLength = 0;\r
652 \r
653     /* Add the "include job document" flag if false. The default value is true,\r
654      * so the flag is not needed if true. */\r
655     if( includeJobDocument == false )\r
656     {\r
657         /* Add the length of "includeJobDocument" plus 4 for 2 quotes, a colon,\r
658          * and a comma. */\r
659         requestLength += INCLUDE_JOB_DOCUMENT_KEY_LENGTH + 4;\r
660 \r
661         /* Add the length of "false". */\r
662         requestLength += 5;\r
663     }\r
664 \r
665     /* Add the length of the execution number if present. */\r
666     if( executionNumber != AWS_IOT_JOBS_NO_EXECUTION_NUMBER )\r
667     {\r
668         /* Convert the execution number to a string. */\r
669         executionNumberLength = snprintf( pExecutionNumber,\r
670                                           EXECUTION_NUMBER_STRING_LENGTH,\r
671                                           "%d",\r
672                                           executionNumber );\r
673         AwsIotJobs_Assert( executionNumberLength > 0 );\r
674         AwsIotJobs_Assert( executionNumberLength < EXECUTION_NUMBER_STRING_LENGTH );\r
675 \r
676         requestLength += EXECUTION_NUMBER_KEY_LENGTH + 4;\r
677         requestLength += ( size_t ) executionNumberLength;\r
678     }\r
679 \r
680     /* Add the length of the client token. */\r
681     if( pRequestInfo->pClientToken != AWS_IOT_JOBS_CLIENT_TOKEN_AUTOGENERATE )\r
682     {\r
683         AwsIotJobs_Assert( pRequestInfo->clientTokenLength > 0 );\r
684 \r
685         requestLength += pRequestInfo->clientTokenLength;\r
686     }\r
687     else\r
688     {\r
689         requestLength += CLIENT_TOKEN_AUTOGENERATE_LENGTH;\r
690     }\r
691 \r
692     /* Allocate memory for the request JSON. */\r
693     pJobsRequest = AwsIotJobs_MallocString( requestLength );\r
694 \r
695     if( pJobsRequest == NULL )\r
696     {\r
697         IotLogError( "No memory for Jobs DESCRIBE request." );\r
698 \r
699         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
700     }\r
701 \r
702     /* Clear the request JSON. */\r
703     ( void ) memset( pJobsRequest, 0x00, requestLength );\r
704 \r
705     /* Construct the request JSON. */\r
706     APPEND_STRING( pJobsRequest, copyOffset, "{\"", 2 );\r
707 \r
708     /* Add the "include job document" flag if false. */\r
709     if( includeJobDocument == false )\r
710     {\r
711         copyOffset = _appendFlag( pJobsRequest,\r
712                                   copyOffset,\r
713                                   INCLUDE_JOB_DOCUMENT_KEY,\r
714                                   INCLUDE_JOB_DOCUMENT_KEY_LENGTH,\r
715                                   false );\r
716     }\r
717 \r
718     /* Add the length of the execution number if present. */\r
719     if( executionNumber != AWS_IOT_JOBS_NO_EXECUTION_NUMBER )\r
720     {\r
721         copyOffset = _appendExecutionNumber( pJobsRequest,\r
722                                              copyOffset,\r
723                                              pExecutionNumber,\r
724                                              ( size_t ) executionNumberLength );\r
725     }\r
726 \r
727     /* Add client token. */\r
728     copyOffset = _appendClientToken( pJobsRequest, copyOffset, pRequestInfo, pOperation );\r
729 \r
730     APPEND_STRING( pJobsRequest, copyOffset, "\"}", 2 );\r
731 \r
732     /* Set the output parameters. */\r
733     pOperation->pJobsRequest = pJobsRequest;\r
734     pOperation->jobsRequestLength = requestLength;\r
735 \r
736     /* Ensure offsets are valid. */\r
737     AwsIotJobs_Assert( copyOffset == requestLength );\r
738     AwsIotJobs_Assert( pOperation->pClientToken > pOperation->pJobsRequest );\r
739     AwsIotJobs_Assert( pOperation->pClientToken <\r
740                        pOperation->pJobsRequest + pOperation->jobsRequestLength );\r
741 \r
742     IotLogDebug( "Jobs DESCRIBE request: %.*s",\r
743                  pOperation->jobsRequestLength,\r
744                  pOperation->pJobsRequest );\r
745 \r
746     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
747 }\r
748 \r
749 /*-----------------------------------------------------------*/\r
750 \r
751 static AwsIotJobsError_t _generateUpdateRequest( const AwsIotJobsRequestInfo_t * pRequestInfo,\r
752                                                  const AwsIotJobsUpdateInfo_t * pUpdateInfo,\r
753                                                  _jobsOperation_t * pOperation )\r
754 {\r
755     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );\r
756     char * pJobsRequest = NULL;\r
757     size_t copyOffset = 0;\r
758     size_t requestLength = MINIMUM_REQUEST_LENGTH;\r
759     const char * pStatus = NULL;\r
760     size_t statusLength = 0;\r
761     char pExpectedVersion[ EXPECTED_VERSION_STRING_LENGTH ] = { 0 };\r
762     char pExecutionNumber[ EXECUTION_NUMBER_STRING_LENGTH ] = { 0 };\r
763     char pStepTimeout[ STEP_TIMEOUT_STRING_LENGTH ] = { 0 };\r
764     int expectedVersionLength = 0, executionNumberLength = 0, stepTimeoutLength = 0;\r
765 \r
766     /* Determine the status string and length to report to the Jobs service.\r
767      * Add 6 for the 4 quotes, colon, and comma. */\r
768     requestLength += STATUS_KEY_LENGTH + 6;\r
769 \r
770     switch( pUpdateInfo->newStatus )\r
771     {\r
772         case AWS_IOT_JOB_STATE_IN_PROGRESS:\r
773             pStatus = "IN_PROGRESS";\r
774             break;\r
775 \r
776         case AWS_IOT_JOB_STATE_FAILED:\r
777             pStatus = "FAILED";\r
778             break;\r
779 \r
780         case AWS_IOT_JOB_STATE_SUCCEEDED:\r
781             pStatus = "SUCCEEDED";\r
782             break;\r
783 \r
784         default:\r
785             /* The only remaining valid state is REJECTED. */\r
786             AwsIotJobs_Assert( pUpdateInfo->newStatus == AWS_IOT_JOB_STATE_REJECTED );\r
787             pStatus = "REJECTED";\r
788             break;\r
789     }\r
790 \r
791     statusLength = strlen( pStatus );\r
792     requestLength += statusLength;\r
793 \r
794     /* Add the length of status details if provided. */\r
795     if( pUpdateInfo->pStatusDetails != AWS_IOT_JOBS_NO_STATUS_DETAILS )\r
796     {\r
797         /* Add 4 for the 2 quotes, colon, and comma. */\r
798         requestLength += STATUS_DETAILS_KEY_LENGTH + 4;\r
799         requestLength += pUpdateInfo->statusDetailsLength;\r
800     }\r
801 \r
802     /* Add the expected version if provided. */\r
803     if( pUpdateInfo->expectedVersion != AWS_IOT_JOBS_NO_VERSION )\r
804     {\r
805         /* Convert the expected version to a string. */\r
806         expectedVersionLength = snprintf( pExpectedVersion,\r
807                                           EXPECTED_VERSION_STRING_LENGTH,\r
808                                           "%u",\r
809                                           pUpdateInfo->expectedVersion );\r
810         AwsIotJobs_Assert( expectedVersionLength > 0 );\r
811         AwsIotJobs_Assert( expectedVersionLength < EXPECTED_VERSION_STRING_LENGTH );\r
812 \r
813         /* Add 6 for the 4 quotes, colon, and comma. */\r
814         requestLength += EXPECTED_VERSION_KEY_LENGTH + 6;\r
815         requestLength += ( size_t ) expectedVersionLength;\r
816     }\r
817 \r
818     /* Add the length of the execution number if present. */\r
819     if( pUpdateInfo->executionNumber != AWS_IOT_JOBS_NO_EXECUTION_NUMBER )\r
820     {\r
821         /* Convert the execution number to a string. */\r
822         executionNumberLength = snprintf( pExecutionNumber,\r
823                                           EXECUTION_NUMBER_STRING_LENGTH,\r
824                                           "%d",\r
825                                           pUpdateInfo->executionNumber );\r
826         AwsIotJobs_Assert( executionNumberLength > 0 );\r
827         AwsIotJobs_Assert( executionNumberLength < EXECUTION_NUMBER_STRING_LENGTH );\r
828 \r
829         requestLength += EXECUTION_NUMBER_KEY_LENGTH + 4;\r
830         requestLength += ( size_t ) executionNumberLength;\r
831     }\r
832 \r
833     /* Add the flags if true. The default values are false, so the flags are not\r
834      * needed if false. */\r
835     if( pUpdateInfo->includeJobExecutionState == true )\r
836     {\r
837         /* Add the length of "includeJobExecutionState" plus 4 for 2 quotes, a colon,\r
838          * and a comma. */\r
839         requestLength += INCLUDE_JOB_EXECUTION_STATE_KEY_LENGTH + 4;\r
840 \r
841         /* Add the length of "true". */\r
842         requestLength += 4;\r
843     }\r
844 \r
845     if( pUpdateInfo->includeJobDocument == true )\r
846     {\r
847         /* Add the length of "includeJobDocument" plus 4 for 2 quotes, a colon,\r
848          * and a comma. */\r
849         requestLength += INCLUDE_JOB_DOCUMENT_KEY_LENGTH + 4;\r
850 \r
851         /* Add the length of "true". */\r
852         requestLength += 4;\r
853     }\r
854 \r
855     /* Add the step timeout if provided. */\r
856     if( pUpdateInfo->stepTimeoutInMinutes != AWS_IOT_JOBS_NO_TIMEOUT )\r
857     {\r
858         /* Calculate the length of the step timeout. Add 4 for the 2 quotes, colon, and comma. */\r
859         requestLength += STEP_TIMEOUT_KEY_LENGTH + 4;\r
860 \r
861         if( pUpdateInfo->stepTimeoutInMinutes == AWS_IOT_JOBS_CANCEL_TIMEOUT )\r
862         {\r
863             /* Step timeout will be set to -1. */\r
864             pStepTimeout[ 0 ] = '-';\r
865             pStepTimeout[ 1 ] = '1';\r
866             stepTimeoutLength = 2;\r
867         }\r
868         else\r
869         {\r
870             /* Convert the step timeout to a string. */\r
871             stepTimeoutLength = snprintf( pStepTimeout,\r
872                                           STEP_TIMEOUT_STRING_LENGTH,\r
873                                           "%d",\r
874                                           pUpdateInfo->stepTimeoutInMinutes );\r
875             AwsIotJobs_Assert( stepTimeoutLength > 0 );\r
876             AwsIotJobs_Assert( stepTimeoutLength < STEP_TIMEOUT_STRING_LENGTH );\r
877         }\r
878 \r
879         requestLength += ( size_t ) stepTimeoutLength;\r
880     }\r
881 \r
882     /* Add the length of the client token. */\r
883     if( pRequestInfo->pClientToken != AWS_IOT_JOBS_CLIENT_TOKEN_AUTOGENERATE )\r
884     {\r
885         AwsIotJobs_Assert( pRequestInfo->clientTokenLength > 0 );\r
886 \r
887         requestLength += pRequestInfo->clientTokenLength;\r
888     }\r
889     else\r
890     {\r
891         requestLength += CLIENT_TOKEN_AUTOGENERATE_LENGTH;\r
892     }\r
893 \r
894     /* Allocate memory for the request JSON. */\r
895     pJobsRequest = AwsIotJobs_MallocString( requestLength );\r
896 \r
897     if( pJobsRequest == NULL )\r
898     {\r
899         IotLogError( "No memory for Jobs UPDATE request." );\r
900 \r
901         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );\r
902     }\r
903 \r
904     /* Clear the request JSON. */\r
905     ( void ) memset( pJobsRequest, 0x00, requestLength );\r
906 \r
907     /* Construct the request JSON. */\r
908     APPEND_STRING( pJobsRequest, copyOffset, "{\"", 2 );\r
909 \r
910     /* Add the status. */\r
911     APPEND_STRING( pJobsRequest, copyOffset, STATUS_KEY, STATUS_KEY_LENGTH );\r
912     APPEND_STRING( pJobsRequest, copyOffset, "\":\"", 3 );\r
913     APPEND_STRING( pJobsRequest, copyOffset, pStatus, statusLength );\r
914     APPEND_STRING( pJobsRequest, copyOffset, "\",\"", 3 );\r
915 \r
916     /* Add status details if present. */\r
917     if( pUpdateInfo->pStatusDetails != AWS_IOT_JOBS_NO_STATUS_DETAILS )\r
918     {\r
919         copyOffset = _appendStatusDetails( pJobsRequest,\r
920                                            copyOffset,\r
921                                            pUpdateInfo->pStatusDetails,\r
922                                            pUpdateInfo->statusDetailsLength );\r
923     }\r
924 \r
925     /* Add expected version. */\r
926     if( pUpdateInfo->expectedVersion != AWS_IOT_JOBS_NO_VERSION )\r
927     {\r
928         APPEND_STRING( pJobsRequest,\r
929                        copyOffset,\r
930                        EXPECTED_VERSION_KEY,\r
931                        EXPECTED_VERSION_KEY_LENGTH );\r
932         APPEND_STRING( pJobsRequest, copyOffset, "\":\"", 3 );\r
933         APPEND_STRING( pJobsRequest, copyOffset, pExpectedVersion, expectedVersionLength );\r
934         APPEND_STRING( pJobsRequest, copyOffset, "\",\"", 3 );\r
935     }\r
936 \r
937     /* Add execution number. */\r
938     if( pUpdateInfo->executionNumber != AWS_IOT_JOBS_NO_EXECUTION_NUMBER )\r
939     {\r
940         copyOffset = _appendExecutionNumber( pJobsRequest,\r
941                                              copyOffset,\r
942                                              pExecutionNumber,\r
943                                              executionNumberLength );\r
944     }\r
945 \r
946     /* Add flags if not default values. */\r
947     if( pUpdateInfo->includeJobExecutionState == true )\r
948     {\r
949         copyOffset = _appendFlag( pJobsRequest,\r
950                                   copyOffset,\r
951                                   INCLUDE_JOB_EXECUTION_STATE_KEY,\r
952                                   INCLUDE_JOB_EXECUTION_STATE_KEY_LENGTH,\r
953                                   true );\r
954     }\r
955 \r
956     if( pUpdateInfo->includeJobDocument == true )\r
957     {\r
958         copyOffset = _appendFlag( pJobsRequest,\r
959                                   copyOffset,\r
960                                   INCLUDE_JOB_DOCUMENT_KEY,\r
961                                   INCLUDE_JOB_DOCUMENT_KEY_LENGTH,\r
962                                   true );\r
963     }\r
964 \r
965     /* Add step timeout if provided. */\r
966     if( pUpdateInfo->stepTimeoutInMinutes != AWS_IOT_JOBS_NO_TIMEOUT )\r
967     {\r
968         copyOffset = _appendStepTimeout( pJobsRequest,\r
969                                          copyOffset,\r
970                                          pStepTimeout,\r
971                                          stepTimeoutLength );\r
972     }\r
973 \r
974     /* Add the client token. */\r
975     copyOffset = _appendClientToken( pJobsRequest, copyOffset, pRequestInfo, pOperation );\r
976 \r
977     APPEND_STRING( pJobsRequest, copyOffset, "\"}", 2 );\r
978 \r
979     /* Set the output parameters. */\r
980     pOperation->pJobsRequest = pJobsRequest;\r
981     pOperation->jobsRequestLength = requestLength;\r
982 \r
983     /* Ensure offsets are valid. */\r
984     AwsIotJobs_Assert( copyOffset == requestLength );\r
985     AwsIotJobs_Assert( pOperation->pClientToken > pOperation->pJobsRequest );\r
986     AwsIotJobs_Assert( pOperation->pClientToken <\r
987                        pOperation->pJobsRequest + pOperation->jobsRequestLength );\r
988 \r
989     IotLogDebug( "Jobs UPDATE request: %.*s",\r
990                  pOperation->jobsRequestLength,\r
991                  pOperation->pJobsRequest );\r
992 \r
993     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
994 }\r
995 \r
996 /*-----------------------------------------------------------*/\r
997 \r
998 static AwsIotJobsError_t _parseErrorDocument( const char * pErrorDocument,\r
999                                               size_t errorDocumentLength )\r
1000 {\r
1001     IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );\r
1002     const char * pCode = NULL;\r
1003     size_t codeLength = 0;\r
1004 \r
1005     /* Find the error code. */\r
1006     if( AwsIotDocParser_FindValue( pErrorDocument,\r
1007                                    errorDocumentLength,\r
1008                                    CODE_KEY,\r
1009                                    CODE_KEY_LENGTH,\r
1010                                    &pCode,\r
1011                                    &codeLength ) == false )\r
1012     {\r
1013         IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_RESPONSE );\r
1014     }\r
1015 \r
1016     /* Match the JSON error code to a Jobs return value. Assume invalid status\r
1017      * unless matched.*/\r
1018     status = AWS_IOT_JOBS_BAD_RESPONSE;\r
1019 \r
1020     switch( codeLength )\r
1021     {\r
1022         /* InvalidJson */\r
1023         case 13:\r
1024 \r
1025             if( strncmp( "\"InvalidJson\"", pCode, codeLength ) == 0 )\r
1026             {\r
1027                 status = AWS_IOT_JOBS_INVALID_JSON;\r
1028             }\r
1029 \r
1030             break;\r
1031 \r
1032         /* InvalidTopic */\r
1033         case 14:\r
1034 \r
1035             if( strncmp( "\"InvalidTopic\"", pCode, codeLength ) == 0 )\r
1036             {\r
1037                 status = AWS_IOT_JOBS_INVALID_TOPIC;\r
1038             }\r
1039 \r
1040             break;\r
1041 \r
1042         /* InternalError */\r
1043         case 15:\r
1044 \r
1045             if( strncmp( "\"InternalError\"", pCode, codeLength ) == 0 )\r
1046             {\r
1047                 status = AWS_IOT_JOBS_INTERNAL_ERROR;\r
1048             }\r
1049 \r
1050             break;\r
1051 \r
1052         /* InvalidRequest */\r
1053         case 16:\r
1054 \r
1055             if( strncmp( "\"InvalidRequest\"", pCode, codeLength ) == 0 )\r
1056             {\r
1057                 status = AWS_IOT_JOBS_INVALID_REQUEST;\r
1058             }\r
1059 \r
1060             break;\r
1061 \r
1062         /* VersionMismatch */\r
1063         case 17:\r
1064 \r
1065             if( strncmp( "\"VersionMismatch\"", pCode, codeLength ) == 0 )\r
1066             {\r
1067                 status = AWS_IOT_JOBS_VERSION_MISMATCH;\r
1068             }\r
1069 \r
1070             break;\r
1071 \r
1072         /* ResourceNotFound, RequestThrottled */\r
1073         case 18:\r
1074 \r
1075             if( strncmp( "\"ResourceNotFound\"", pCode, codeLength ) == 0 )\r
1076             {\r
1077                 status = AWS_IOT_JOBS_NOT_FOUND;\r
1078             }\r
1079             else if( strncmp( "\"RequestThrottled\"", pCode, codeLength ) == 0 )\r
1080             {\r
1081                 status = AWS_IOT_JOBS_THROTTLED;\r
1082             }\r
1083 \r
1084             break;\r
1085 \r
1086         /* TerminalStateReached */\r
1087         case 22:\r
1088 \r
1089             if( strncmp( "\"TerminalStateReached\"", pCode, codeLength ) == 0 )\r
1090             {\r
1091                 status = AWS_IOT_JOBS_TERMINAL_STATE;\r
1092             }\r
1093 \r
1094             break;\r
1095 \r
1096         /* InvalidStateTransition */\r
1097         case 24:\r
1098 \r
1099             if( strncmp( "\"InvalidStateTransition\"", pCode, codeLength ) == 0 )\r
1100             {\r
1101                 status = AWS_IOT_JOBS_INVALID_STATE;\r
1102             }\r
1103 \r
1104             break;\r
1105 \r
1106         default:\r
1107             break;\r
1108     }\r
1109 \r
1110     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
1111 }\r
1112 \r
1113 /*-----------------------------------------------------------*/\r
1114 \r
1115 AwsIotJobsError_t _AwsIotJobs_GenerateJsonRequest( _jobsOperationType_t type,\r
1116                                                    const AwsIotJobsRequestInfo_t * pRequestInfo,\r
1117                                                    const _jsonRequestContents_t * pRequestContents,\r
1118                                                    _jobsOperation_t * pOperation )\r
1119 {\r
1120     AwsIotJobsError_t status = AWS_IOT_JOBS_STATUS_PENDING;\r
1121 \r
1122     /* Generate request based on the Job operation type. */\r
1123     switch( type )\r
1124     {\r
1125         case JOBS_GET_PENDING:\r
1126             status = _generateGetPendingRequest( pRequestInfo, pOperation );\r
1127             break;\r
1128 \r
1129         case JOBS_START_NEXT:\r
1130             status = _generateStartNextRequest( pRequestInfo,\r
1131                                                 pRequestContents->pUpdateInfo,\r
1132                                                 pOperation );\r
1133             break;\r
1134 \r
1135         case JOBS_DESCRIBE:\r
1136             status = _generateDescribeRequest( pRequestInfo,\r
1137                                                pRequestContents->describe.executionNumber,\r
1138                                                pRequestContents->describe.includeJobDocument,\r
1139                                                pOperation );\r
1140             break;\r
1141 \r
1142         default:\r
1143             /* The only remaining valid type is UPDATE. */\r
1144             AwsIotJobs_Assert( type == JOBS_UPDATE );\r
1145 \r
1146             status = _generateUpdateRequest( pRequestInfo,\r
1147                                              pRequestContents->pUpdateInfo,\r
1148                                              pOperation );\r
1149             break;\r
1150     }\r
1151 \r
1152     return status;\r
1153 }\r
1154 \r
1155 /*-----------------------------------------------------------*/\r
1156 \r
1157 void _AwsIotJobs_ParseResponse( AwsIotStatus_t status,\r
1158                                 const char * pResponse,\r
1159                                 size_t responseLength,\r
1160                                 _jobsOperation_t * pOperation )\r
1161 {\r
1162     AwsIotJobs_Assert( pOperation->status == AWS_IOT_JOBS_STATUS_PENDING );\r
1163 \r
1164     /* A non-waitable operation can re-use the pointers from the publish info,\r
1165      * since those are guaranteed to be in-scope throughout the user callback.\r
1166      * But a waitable operation must copy the data from the publish info because\r
1167      * AwsIotJobs_Wait may be called after the MQTT library frees the publish\r
1168      * info. */\r
1169     if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == 0 )\r
1170     {\r
1171         pOperation->pJobsResponse = pResponse;\r
1172         pOperation->jobsResponseLength = responseLength;\r
1173     }\r
1174     else\r
1175     {\r
1176         IotLogDebug( "Allocating new buffer for waitable Jobs %s.",\r
1177                      _pAwsIotJobsOperationNames[ pOperation->type ] );\r
1178 \r
1179         /* Parameter validation should not have allowed a NULL malloc function. */\r
1180         AwsIotJobs_Assert( pOperation->mallocResponse != NULL );\r
1181 \r
1182         /* Allocate a buffer for the retrieved document. */\r
1183         pOperation->pJobsResponse = pOperation->mallocResponse( responseLength );\r
1184 \r
1185         if( pOperation->pJobsResponse == NULL )\r
1186         {\r
1187             IotLogError( "Failed to allocate buffer for retrieved Jobs %s response.",\r
1188                          _pAwsIotJobsOperationNames[ pOperation->type ] );\r
1189 \r
1190             pOperation->status = AWS_IOT_JOBS_NO_MEMORY;\r
1191         }\r
1192         else\r
1193         {\r
1194             /* Copy the response. */\r
1195             ( void ) memcpy( ( void * ) pOperation->pJobsResponse, pResponse, responseLength );\r
1196             pOperation->jobsResponseLength = responseLength;\r
1197         }\r
1198     }\r
1199 \r
1200     /* Set the status of the Jobs operation. */\r
1201     if( pOperation->status == AWS_IOT_JOBS_STATUS_PENDING )\r
1202     {\r
1203         if( status == AWS_IOT_ACCEPTED )\r
1204         {\r
1205             pOperation->status = AWS_IOT_JOBS_SUCCESS;\r
1206         }\r
1207         else\r
1208         {\r
1209             pOperation->status = _parseErrorDocument( pResponse, responseLength );\r
1210         }\r
1211     }\r
1212 }\r
1213 \r
1214 /*-----------------------------------------------------------*/\r