in device_firmware/libraries/freertos_plus/aws/ota/src/aws_iot_ota_agent.c [2099:2410]
static DocParseErr_t prvParseJSONbyModel( const char * pcJSON,
uint32_t ulMsgLen,
JSON_DocModel_t * pxDocModel )
{
DEFINE_OTA_METHOD_NAME( "prvParseJSONbyModel" );
const JSON_DocParam_t * pxModelParam;
jsmn_parser xParser;
jsmntok_t * pxTokens, * pxValTok;
uint32_t ulNumTokens, ulTokenLen;
MultiParmPtr_t xParamAddr; /*lint !e9018 We intentionally use this union to cast the parameter address to the proper type. */
uint32_t ulIndex;
uint16_t usModelParamIndex;
uint32_t ulScanIndex;
DocParseErr_t eErr = eDocParseErr_Unknown;
/* Reset the Jasmine tokenizer. */
jsmn_init( &xParser );
/* Validate some initial parameters. */
if( pxDocModel == NULL )
{
OTA_LOG_L1( "[%s] The pointer to the document model is NULL.\r\n", OTA_METHOD_NAME );
eErr = eDocParseErr_NullModelPointer;
}
else if( pxDocModel->pxBodyDef == NULL )
{
OTA_LOG_L1( "[%s] Document model 0x%08x body pointer is NULL.\r\n", OTA_METHOD_NAME, pxDocModel );
eErr = eDocParseErr_NullBodyPointer;
}
else if( pxDocModel->usNumModelParams > OTA_DOC_MODEL_MAX_PARAMS )
{
OTA_LOG_L1( "[%s] Model has too many parameters (%u).\r\n", OTA_METHOD_NAME, pxDocModel->usNumModelParams );
eErr = eDocParseErr_TooManyParams;
}
else if( pcJSON == NULL )
{
OTA_LOG_L1( "[%s] JSON document pointer is NULL!\r\n", OTA_METHOD_NAME );
eErr = eDocParseErr_NullDocPointer;
}
else
{
pxModelParam = pxDocModel->pxBodyDef;
/* Count the total number of tokens in our JSON document. */
ulNumTokens = ( uint32_t ) jsmn_parse( &xParser, pcJSON, ( size_t ) ulMsgLen, NULL, 1UL );
if( ulNumTokens > 0U )
{
/* If the JSON document isn't too big for our token array... */
if( ulNumTokens <= OTA_MAX_JSON_TOKENS )
{
/* Allocate space for the document JSON tokens. */
void * pvTokenArray = pvPortMalloc( ulNumTokens * sizeof( jsmntok_t ) ); /* Allocate space on heap for temporary token array. */
pxTokens = ( jsmntok_t * ) pvTokenArray; /*lint !e9079 !e9087 heap allocations return void* so we allow casting to a pointer to the actual type. */
if( pxTokens != NULL )
{
/* Reset Jasmine again and tokenize the document for real. */
jsmn_init( &xParser );
ulIndex = ( uint32_t ) jsmn_parse( &xParser, pcJSON, ulMsgLen, pxTokens, ulNumTokens );
if( ulIndex == ulNumTokens )
{
/* Start the parser in an error free state. */
eErr = eDocParseErr_None;
/* Examine each JSON token, searching for job parameters based on our document model. */
for( ulIndex = 0U; ( eErr == eDocParseErr_None ) && ( ulIndex < ulNumTokens ); ulIndex++ )
{
/* All parameter keys are JSON strings. */
if( pxTokens[ ulIndex ].type == JSMN_STRING )
{
/* Search the document model to see if it matches the current key. */
ulTokenLen = ( uint32_t ) pxTokens[ ulIndex ].end - ( uint32_t ) pxTokens[ ulIndex ].start;
eErr = prvSearchModelForTokenKey( pxDocModel, &pcJSON[ pxTokens[ ulIndex ].start ], ulTokenLen, &usModelParamIndex );
/* If we didn't find a match in the model, skip over it and its descendants. */
if( eErr == eDocParseErr_ParamKeyNotInModel )
{
int32_t iRoot = ( int32_t ) ulIndex; /* Create temp root from the unrecognized tokens index. Use signed int since the parent index is signed. */
ulIndex++; /* Skip the active key since it's the one we don't recognize. */
/* Skip tokens whose parents are equal to or deeper than the unrecognized temporary root token level. */
while( ( ulIndex < ulNumTokens ) && ( pxTokens[ ulIndex ].parent >= iRoot ) )
{
ulIndex++; /* Skip over all descendants of the unknown parent. */
}
--ulIndex; /* Adjust for outer for-loop increment. */
eErr = eDocParseErr_None; /* Unknown key structures are simply skipped so clear the error state to continue. */
}
else if( eErr == eDocParseErr_None )
{
/* We found the parameter key in the document model. */
/* Get the value field (i.e. the following token) for the parameter. */
pxValTok = &pxTokens[ ulIndex + 1UL ];
/* Verify the field type is what we expect for this parameter. */
if( pxValTok->type != pxModelParam[ usModelParamIndex ].eJasmineType )
{
ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start );
OTA_LOG_L1( "[%s] parameter type mismatch [ %s : %.*s ] type %u, expected %u\r\n",
OTA_METHOD_NAME, pxModelParam[ usModelParamIndex ].pcSrcKey, ulTokenLen,
&pcJSON[ pxValTok->start ],
pxValTok->type, pxModelParam[ usModelParamIndex ].eJasmineType );
eErr = eDocParseErr_FieldTypeMismatch;
/* break; */
}
else if( OTA_DONT_STORE_PARAM == pxModelParam[ usModelParamIndex ].ulDestOffset )
{
/* Nothing to do with this parameter since we're not storing it. */
continue;
}
else
{
/* Get destination offset to parameter storage location. */
/* If it's within the models context structure, add in the context instance base address. */
if( pxModelParam[ usModelParamIndex ].ulDestOffset < pxDocModel->ulContextSize )
{
xParamAddr.ulVal = pxDocModel->ulContextBase + pxModelParam[ usModelParamIndex ].ulDestOffset;
}
else
{
/* It's a raw pointer so keep it as is. */
xParamAddr.ulVal = pxModelParam[ usModelParamIndex ].ulDestOffset;
}
if( eModelParamType_StringCopy == pxModelParam[ usModelParamIndex ].xModelParamType )
{
/* Malloc memory for a copy of the value string plus a zero terminator. */
ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start );
void * pvStringCopy = pvPortMalloc( ulTokenLen + 1U );
if( pvStringCopy != NULL )
{
*xParamAddr.ppvPtr = pvStringCopy;
char * pcStringCopy = *xParamAddr.ppcPtr;
/* Copy parameter string into newly allocated memory. */
memcpy( pcStringCopy, &pcJSON[ pxValTok->start ], ulTokenLen );
/* Zero terminate the new string. */
pcStringCopy[ ulTokenLen ] = '\0';
OTA_LOG_L1( "[%s] Extracted parameter [ %s: %s ]\r\n",
OTA_METHOD_NAME,
pxModelParam[ usModelParamIndex ].pcSrcKey,
pcStringCopy );
}
else
{ /* Stop processing on error. */
eErr = eDocParseErr_OutOfMemory;
/* break; */
}
}
else if( eModelParamType_StringInDoc == pxModelParam[ usModelParamIndex ].xModelParamType )
{
/* Copy pointer to source string instead of duplicating the string. */
const char * pcStringInDoc = &pcJSON[ pxValTok->start ];
if( pcStringInDoc != NULL ) /*lint !e774 This can result in NULL if offset rolls the address around. */
{
*xParamAddr.ppccPtr = pcStringInDoc;
ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start );
OTA_LOG_L1( "[%s] Extracted parameter [ %s: %.*s ]\r\n",
OTA_METHOD_NAME,
pxModelParam[ usModelParamIndex ].pcSrcKey,
ulTokenLen, pcStringInDoc );
}
else
{
/* This should never happen unless there's a bug or memory is corrupted. */
OTA_LOG_L1( "[%s] Error! JSON token produced a null pointer for parameter [ %s ]\r\n",
OTA_METHOD_NAME,
pxModelParam[ usModelParamIndex ].pcSrcKey );
eErr = eDocParseErr_InvalidToken;
}
}
else if( eModelParamType_UInt32 == pxModelParam[ usModelParamIndex ].xModelParamType )
{
char * pEnd;
const char * pStart = &pcJSON[ pxValTok->start ];
*xParamAddr.pulPtr = strtoul( pStart, &pEnd, 0 );
if( pEnd == &pcJSON[ pxValTok->end ] )
{
OTA_LOG_L1( "[%s] Extracted parameter [ %s: %u ]\r\n",
OTA_METHOD_NAME,
pxModelParam[ usModelParamIndex ].pcSrcKey,
*xParamAddr.pulPtr );
}
else
{
eErr = eDocParseErr_InvalidNumChar;
}
}
else if( eModelParamType_SigBase64 == pxModelParam[ usModelParamIndex ].xModelParamType )
{
/* Allocate space for and decode the base64 signature. */
void * pvSignature = pvPortMalloc( sizeof( Sig256_t ) );
if( pvSignature != NULL )
{
size_t xActualLen;
*xParamAddr.ppvPtr = pvSignature;
Sig256_t * pxSig256 = *xParamAddr.ppxSig256Ptr;
ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start );
if( mbedtls_base64_decode( pxSig256->ucData, sizeof( pxSig256->ucData ), &xActualLen,
( const uint8_t * ) &pcJSON[ pxValTok->start ], ulTokenLen ) != 0 )
{ /* Stop processing on error. */
OTA_LOG_L1( "[%s] mbedtls_base64_decode failed.\r\n", OTA_METHOD_NAME );
eErr = eDocParseErr_Base64Decode;
/* break; */
}
else
{
pxSig256->usSize = ( uint16_t ) xActualLen;
OTA_LOG_L1( "[%s] Extracted parameter [ %s: %.32s... ]\r\n",
OTA_METHOD_NAME,
pxModelParam[ usModelParamIndex ].pcSrcKey,
&pcJSON[ pxValTok->start ] );
}
}
else
{
/* We failed to allocate needed memory. Everything will be freed below upon failure. */
eErr = eDocParseErr_OutOfMemory;
}
}
else if( eModelParamType_Ident == pxModelParam[ usModelParamIndex ].xModelParamType )
{
OTA_LOG_L1( "[%s] Identified parameter [ %s ]\r\n",
OTA_METHOD_NAME,
pxModelParam[ usModelParamIndex ].pcSrcKey );
*xParamAddr.pxBoolPtr = pdTRUE;
}
else
{
/* Ignore invalid document model type. */
}
}
}
else
{
/* Nothing special to do. The error will break us out of the loop. */
}
}
else
{
/* Ignore tokens that are not strings and move on to the next. */
}
}
/* Free the token memory. */
vPortFree( pxTokens ); /*lint !e850 ulIndex is intentionally modified within the loop to skip over unknown tags. */
if( eErr == eDocParseErr_None )
{
uint32_t ulMissingParams = ( pxDocModel->ulParamsReceivedBitmap & pxDocModel->ulParamsRequiredBitmap )
^ pxDocModel->ulParamsRequiredBitmap;
if( ulMissingParams != 0U )
{
/* The job document did not have all required document model parameters. */
for( ulScanIndex = 0UL; ulScanIndex < pxDocModel->usNumModelParams; ulScanIndex++ )
{
if( ( ulMissingParams & ( 1UL << ulScanIndex ) ) != 0UL )
{
OTA_LOG_L1( "[%s] parameter not present: %s\r\n",
OTA_METHOD_NAME,
pxModelParam[ ulScanIndex ].pcSrcKey );
}
}
eErr = eDocParseErr_MalformedDoc;
}
}
else
{
OTA_LOG_L1( "[%s] Error (%d) parsing JSON document.\r\n", OTA_METHOD_NAME, ( int32_t ) eErr );
}
}
else
{
OTA_LOG_L1( "[%s] jsmn_parse didn't match token count when parsing.\r\n", OTA_METHOD_NAME );
eErr = eDocParseErr_JasmineCountMismatch;
}
}
else
{
OTA_LOG_L1( "[%s] No memory for JSON tokens.\r\n", OTA_METHOD_NAME );
eErr = eDocParseErr_OutOfMemory;
}
}
else
{
OTA_LOG_L1( "[%s] Document has too many keys.\r\n", OTA_METHOD_NAME );
eErr = eDocParseErr_TooManyTokens;
}
}
else
{
OTA_LOG_L1( "[%s] Invalid JSON document. No tokens parsed. \r\n", OTA_METHOD_NAME );
eErr = eDocParseErr_NoTokens;
}
}
configASSERT( eErr != eDocParseErr_Unknown );
return eErr;
}