static DocParseErr_t prvParseJSONbyModel()

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;
}