--- /dev/null
+/*\r
+ * AWS IoT Shadow V2.1.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ */\r
+\r
+/**\r
+ * @file aws_iot_shadow_api.c\r
+ * @brief Implements the user-facing functions of the Shadow library.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+\r
+/* Shadow internal include. */\r
+#include "private/aws_iot_shadow_internal.h"\r
+\r
+/* Error handling include. */\r
+#include "iot_error.h"\r
+\r
+/* Platform layer includes. */\r
+#include "platform/iot_threads.h"\r
+\r
+/* MQTT include. */\r
+#include "iot_mqtt.h"\r
+\r
+/* Validate Shadow configuration settings. */\r
+#if AWS_IOT_SHADOW_ENABLE_ASSERTS != 0 && AWS_IOT_SHADOW_ENABLE_ASSERTS != 1\r
+ #error "AWS_IOT_SHADOW_ENABLE_ASSERTS must be 0 or 1."\r
+#endif\r
+#if AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS <= 0\r
+ #error "AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS cannot be 0 or negative."\r
+#endif\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Check if the library is initialized.\r
+ *\r
+ * @return `true` if AwsIotShadow_Init was called; `false` otherwise.\r
+ */\r
+static bool _checkInit( void );\r
+\r
+/**\r
+ * @brief Checks Thing Name and flags parameters passed to Shadow API functions.\r
+ *\r
+ * @param[in] type The Shadow API function type.\r
+ * @param[in] pThingName Thing Name passed to Shadow API function.\r
+ * @param[in] thingNameLength Length of `pThingName`.\r
+ * @param[in] flags Flags passed to Shadow API function.\r
+ * @param[in] pCallbackInfo Callback info passed to Shadow API function.\r
+ * @param[in] pOperation Operation reference pointer passed to Shadow API function.\r
+ *\r
+ * @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_BAD_PARAMETER.\r
+ */\r
+static AwsIotShadowError_t _validateThingNameFlags( _shadowOperationType_t type,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ uint32_t flags,\r
+ const AwsIotShadowCallbackInfo_t * pCallbackInfo,\r
+ const AwsIotShadowOperation_t * pOperation );\r
+\r
+/**\r
+ * @brief Checks document info parameter passed to Shadow API functions.\r
+ *\r
+ * @param[in] type The Shadow API function type.\r
+ * @param[in] flags Flags passed to Shadow API function.\r
+ * @param[in] pDocumentInfo Document info passed to Shadow API function.\r
+ *\r
+ * @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_BAD_PARAMETER.\r
+ */\r
+static AwsIotShadowError_t _validateDocumentInfo( _shadowOperationType_t type,\r
+ uint32_t flags,\r
+ const AwsIotShadowDocumentInfo_t * pDocumentInfo );\r
+\r
+/**\r
+ * @brief Common function for setting Shadow callbacks.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to use.\r
+ * @param[in] type Type of Shadow callback.\r
+ * @param[in] pThingName Thing Name for Shadow callback.\r
+ * @param[in] thingNameLength Length of `pThingName`.\r
+ * @param[in] pCallbackInfo Callback information to set.\r
+ *\r
+ * @return #AWS_IOT_SHADOW_SUCCESS, #AWS_IOT_SHADOW_BAD_PARAMETER,\r
+ * #AWS_IOT_SHADOW_NO_MEMORY, or #AWS_IOT_SHADOW_MQTT_ERROR.\r
+ */\r
+static AwsIotShadowError_t _setCallbackCommon( IotMqttConnection_t mqttConnection,\r
+ _shadowCallbackType_t type,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ const AwsIotShadowCallbackInfo_t * pCallbackInfo );\r
+\r
+/**\r
+ * @brief Change the subscriptions for Shadow callbacks, either by subscribing\r
+ * or unsubscribing.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to use.\r
+ * @param[in] type Type of Shadow callback.\r
+ * @param[in] pSubscription Shadow subscriptions object for callback.\r
+ * @param[in] mqttOperation Either @ref mqtt_function_subscribesync or\r
+ * @ref mqtt_function_unsubscribesync.\r
+ *\r
+ * @return #AWS_IOT_SHADOW_SUCCESS, #AWS_IOT_SHADOW_NO_MEMORY, or\r
+ * #AWS_IOT_SHADOW_MQTT_ERROR.\r
+ */\r
+static AwsIotShadowError_t _modifyCallbackSubscriptions( IotMqttConnection_t mqttConnection,\r
+ _shadowCallbackType_t type,\r
+ _shadowSubscription_t * pSubscription,\r
+ AwsIotMqttFunction_t mqttOperation );\r
+\r
+/**\r
+ * @brief Common function for incoming Shadow callbacks.\r
+ *\r
+ * @param[in] type Shadow callback type.\r
+ * @param[in] pMessage The received Shadow callback document (as an MQTT PUBLISH\r
+ * message).\r
+ */\r
+static void _callbackWrapperCommon( _shadowCallbackType_t type,\r
+ IotMqttCallbackParam_t * pMessage );\r
+\r
+/**\r
+ * @brief Invoked when a document is received on the Shadow DELTA callback.\r
+ *\r
+ * @param[in] pArgument Ignored.\r
+ * @param[in] pMessage The received DELTA document (as an MQTT PUBLISH message).\r
+ */\r
+static void _deltaCallbackWrapper( void * pArgument,\r
+ IotMqttCallbackParam_t * pMessage );\r
+\r
+/**\r
+ * @brief Invoked when a document is received on the Shadow UPDATED callback.\r
+ *\r
+ * @param[in] pArgument Ignored.\r
+ * @param[in] pMessage The received UPDATED document (as an MQTT PUBLISH message).\r
+ */\r
+static void _updatedCallbackWrapper( void * pArgument,\r
+ IotMqttCallbackParam_t * pMessage );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Tracks whether @ref shadow_function_init has been called.\r
+ *\r
+ * API functions will fail if @ref shadow_function_init was not called.\r
+ */\r
+static uint32_t _initCalled = 0;\r
+\r
+/**\r
+ * @brief Timeout used for MQTT operations.\r
+ */\r
+uint32_t _AwsIotShadowMqttTimeoutMs = AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS;\r
+\r
+#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE\r
+\r
+/**\r
+ * @brief Printable names for the Shadow callbacks.\r
+ */\r
+ const char * const _pAwsIotShadowCallbackNames[] =\r
+ {\r
+ "DELTA",\r
+ "UPDATED"\r
+ };\r
+#endif\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _checkInit( void )\r
+{\r
+ bool status = true;\r
+\r
+ if( _initCalled == 0 )\r
+ {\r
+ IotLogError( "AwsIotShadow_Init was not called." );\r
+\r
+ status = false;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static AwsIotShadowError_t _validateThingNameFlags( _shadowOperationType_t type,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ uint32_t flags,\r
+ const AwsIotShadowCallbackInfo_t * pCallbackInfo,\r
+ const AwsIotShadowOperation_t * pOperation )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );\r
+\r
+ /* Type is not used when logging is disabled. */\r
+ ( void ) type;\r
+\r
+ /* Check Thing Name. */\r
+ if( AwsIot_ValidateThingName( pThingName, thingNameLength ) == false )\r
+ {\r
+ IotLogError( "Thing Name for Shadow %s is not valid.",\r
+ _pAwsIotShadowOperationNames[ type ] );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+\r
+ /* Check the waitable operation flag. */\r
+ if( ( flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )\r
+ {\r
+ /* Check that a reference pointer is provided for a waitable operation. */\r
+ if( pOperation == NULL )\r
+ {\r
+ IotLogError( "Reference must be set for a waitable Shadow %s.",\r
+ _pAwsIotShadowOperationNames[ type ] );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+\r
+ /* A callback should not be set for a waitable operation. */\r
+ if( pCallbackInfo != NULL )\r
+ {\r
+ IotLogError( "Callback should not be set for a waitable Shadow %s.",\r
+ _pAwsIotShadowOperationNames[ type ] );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+ }\r
+\r
+ /* A callback info must be passed to a non-waitable GET. */\r
+ if( ( type == SHADOW_GET ) &&\r
+ ( ( flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == 0 ) &&\r
+ ( pCallbackInfo == NULL ) )\r
+ {\r
+ IotLogError( "Callback info must be provided for non-waitable Shadow GET." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+\r
+ /* Check that a callback function is set. */\r
+ if( ( pCallbackInfo != NULL ) &&\r
+ ( pCallbackInfo->function == NULL ) )\r
+ {\r
+ IotLogError( "Callback function must be set for Shadow %s callback.",\r
+ _pAwsIotShadowOperationNames[ type ] );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static AwsIotShadowError_t _validateDocumentInfo( _shadowOperationType_t type,\r
+ uint32_t flags,\r
+ const AwsIotShadowDocumentInfo_t * pDocumentInfo )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );\r
+\r
+ /* This function should only be called for Shadow GET or UPDATE. */\r
+ AwsIotShadow_Assert( ( type == SHADOW_GET ) || ( type == SHADOW_UPDATE ) );\r
+\r
+ /* Check QoS. */\r
+ if( pDocumentInfo->qos != IOT_MQTT_QOS_0 )\r
+ {\r
+ if( pDocumentInfo->qos != IOT_MQTT_QOS_1 )\r
+ {\r
+ IotLogError( "QoS for Shadow %d must be 0 or 1.",\r
+ _pAwsIotShadowOperationNames[ type ] );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+ }\r
+\r
+ /* Check the retry parameters. */\r
+ if( pDocumentInfo->retryLimit > 0 )\r
+ {\r
+ if( pDocumentInfo->retryMs == 0 )\r
+ {\r
+ IotLogError( "Retry time of Shadow %s must be positive.",\r
+ _pAwsIotShadowOperationNames[ type ] );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+ }\r
+\r
+ /* Check members relevant to a Shadow GET. */\r
+ if( type == SHADOW_GET )\r
+ {\r
+ /* Check memory allocation function for waitable GET. */\r
+ if( ( ( flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE ) &&\r
+ ( pDocumentInfo->u.get.mallocDocument == NULL ) )\r
+ {\r
+ IotLogError( "Memory allocation function must be set for waitable Shadow GET." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+ }\r
+ /* Check members relevant to a Shadow UPDATE. */\r
+ else\r
+ {\r
+ /* Check UPDATE document pointer and length. */\r
+ if( ( pDocumentInfo->u.update.pUpdateDocument == NULL ) ||\r
+ ( pDocumentInfo->u.update.updateDocumentLength == 0 ) )\r
+ {\r
+ IotLogError( "Shadow document for Shadow UPDATE cannot be NULL or"\r
+ " have length 0." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static AwsIotShadowError_t _setCallbackCommon( IotMqttConnection_t mqttConnection,\r
+ _shadowCallbackType_t type,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ const AwsIotShadowCallbackInfo_t * pCallbackInfo )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );\r
+ bool subscriptionMutexLocked = false;\r
+ _shadowSubscription_t * pSubscription = NULL;\r
+\r
+ /* Check that AwsIotShadow_Init was called. */\r
+ if( _checkInit() == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NOT_INITIALIZED );\r
+ }\r
+\r
+ /* Check parameters. */\r
+ status = _validateThingNameFlags( ( _shadowOperationType_t ) ( type + SHADOW_OPERATION_COUNT ),\r
+ pThingName,\r
+ thingNameLength,\r
+ 0,\r
+ pCallbackInfo,\r
+ NULL );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ IotLogInfo( "(%.*s) Modifying Shadow %s callback.",\r
+ thingNameLength,\r
+ pThingName,\r
+ _pAwsIotShadowCallbackNames[ type ] );\r
+\r
+ /* Lock the subscription list mutex to check for an existing subscription\r
+ * object. */\r
+ IotMutex_Lock( &( _AwsIotShadowSubscriptionsMutex ) );\r
+ subscriptionMutexLocked = true;\r
+\r
+ /* Check for an existing subscription. This function will attempt to allocate\r
+ * a new subscription if not found. */\r
+ pSubscription = _AwsIotShadow_FindSubscription( pThingName,\r
+ thingNameLength,\r
+ true );\r
+\r
+ if( pSubscription == NULL )\r
+ {\r
+ /* No existing subscription was found, and no new subscription could be\r
+ * allocated. */\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );\r
+ }\r
+\r
+ /* Check for an existing callback. */\r
+ if( pSubscription->callbacks[ type ].function != NULL )\r
+ {\r
+ /* Replace existing callback. */\r
+ if( pCallbackInfo != NULL )\r
+ {\r
+ IotLogInfo( "(%.*s) Found existing %s callback. Replacing callback.",\r
+ thingNameLength,\r
+ pThingName,\r
+ _pAwsIotShadowCallbackNames[ type ] );\r
+\r
+ pSubscription->callbacks[ type ] = *pCallbackInfo;\r
+ }\r
+ /* Remove existing callback. */\r
+ else\r
+ {\r
+ IotLogInfo( "(%.*s) Removing existing %s callback.",\r
+ thingNameLength,\r
+ pThingName,\r
+ _pAwsIotShadowCallbackNames[ type ] );\r
+\r
+ /* Unsubscribe, then clear the callback information. */\r
+ ( void ) _modifyCallbackSubscriptions( mqttConnection,\r
+ type,\r
+ pSubscription,\r
+ IotMqtt_UnsubscribeSync );\r
+ ( void ) memset( &( pSubscription->callbacks[ type ] ),\r
+ 0x00,\r
+ sizeof( AwsIotShadowCallbackInfo_t ) );\r
+\r
+ /* Check if this subscription object can be removed. */\r
+ _AwsIotShadow_RemoveSubscription( pSubscription, NULL );\r
+ }\r
+ }\r
+ /* No existing callback. */\r
+ else\r
+ {\r
+ /* Add new callback. */\r
+ if( pCallbackInfo != NULL )\r
+ {\r
+ IotLogInfo( "(%.*s) Adding new %s callback.",\r
+ thingNameLength,\r
+ pThingName,\r
+ _pAwsIotShadowCallbackNames[ type ] );\r
+\r
+ status = _modifyCallbackSubscriptions( mqttConnection,\r
+ type,\r
+ pSubscription,\r
+ IotMqtt_SubscribeSync );\r
+\r
+ if( status == AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ pSubscription->callbacks[ type ] = *pCallbackInfo;\r
+ }\r
+ else\r
+ {\r
+ /* On failure, check if this subscription can be removed. */\r
+ _AwsIotShadow_RemoveSubscription( pSubscription, NULL );\r
+ }\r
+ }\r
+ /* Do nothing; set return value to success. */\r
+ else\r
+ {\r
+ status = AWS_IOT_SHADOW_SUCCESS;\r
+ }\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ if( subscriptionMutexLocked == true )\r
+ {\r
+ IotMutex_Unlock( &( _AwsIotShadowSubscriptionsMutex ) );\r
+ }\r
+\r
+ IotLogInfo( "(%.*s) Shadow %s callback operation complete with result %s.",\r
+ thingNameLength,\r
+ pThingName,\r
+ _pAwsIotShadowCallbackNames[ type ],\r
+ AwsIotShadow_strerror( status ) );\r
+\r
+ IOT_FUNCTION_CLEANUP_END();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static AwsIotShadowError_t _modifyCallbackSubscriptions( IotMqttConnection_t mqttConnection,\r
+ _shadowCallbackType_t type,\r
+ _shadowSubscription_t * pSubscription,\r
+ AwsIotMqttFunction_t mqttOperation )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );\r
+ IotMqttError_t mqttStatus = IOT_MQTT_STATUS_PENDING;\r
+ IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;\r
+ char * pTopicFilter = NULL;\r
+ uint16_t operationTopicLength = 0;\r
+\r
+ /* Lookup table for Shadow callback suffixes. */\r
+ const char * const pCallbackSuffix[ SHADOW_CALLBACK_COUNT ] =\r
+ {\r
+ SHADOW_DELTA_SUFFIX, /* Delta callback. */\r
+ SHADOW_UPDATED_SUFFIX /* Updated callback. */\r
+ };\r
+\r
+ /* Lookup table for Shadow callback suffix lengths. */\r
+ const uint16_t pCallbackSuffixLength[ SHADOW_CALLBACK_COUNT ] =\r
+ {\r
+ SHADOW_DELTA_SUFFIX_LENGTH, /* Delta callback. */\r
+ SHADOW_UPDATED_SUFFIX_LENGTH /* Updated callback. */\r
+ };\r
+\r
+ /* Lookup table for Shadow callback function wrappers. */\r
+ const AwsIotMqttCallbackFunction_t pCallbackWrapper[ SHADOW_CALLBACK_COUNT ] =\r
+ {\r
+ _deltaCallbackWrapper, /* Delta callback. */\r
+ _updatedCallbackWrapper, /* Updated callback. */\r
+ };\r
+\r
+ /* MQTT operation may only be subscribe or unsubscribe. */\r
+ AwsIotShadow_Assert( ( mqttOperation == IotMqtt_SubscribeSync ) ||\r
+ ( mqttOperation == IotMqtt_UnsubscribeSync ) );\r
+\r
+ /* Use the subscription's topic buffer if available. */\r
+ if( pSubscription->pTopicBuffer != NULL )\r
+ {\r
+ pTopicFilter = pSubscription->pTopicBuffer;\r
+ }\r
+\r
+ /* Generate the prefix portion of the Shadow callback topic filter. Both\r
+ * callbacks share the same callback as the Shadow Update operation. */\r
+ status = _AwsIotShadow_GenerateShadowTopic( SHADOW_UPDATE,\r
+ pSubscription->pThingName,\r
+ pSubscription->thingNameLength,\r
+ &pTopicFilter,\r
+ &operationTopicLength );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Place the callback suffix in the topic filter. */\r
+ ( void ) memcpy( pTopicFilter + operationTopicLength,\r
+ pCallbackSuffix[ type ],\r
+ pCallbackSuffixLength[ type ] );\r
+\r
+ IotLogDebug( "%s subscription for %.*s",\r
+ mqttOperation == IotMqtt_SubscribeSync ? "Adding" : "Removing",\r
+ operationTopicLength + pCallbackSuffixLength[ type ],\r
+ pTopicFilter );\r
+\r
+ /* Set the members of the MQTT subscription. */\r
+ subscription.qos = IOT_MQTT_QOS_1;\r
+ subscription.pTopicFilter = pTopicFilter;\r
+ subscription.topicFilterLength = ( uint16_t ) ( operationTopicLength + pCallbackSuffixLength[ type ] );\r
+ subscription.callback.pCallbackContext = NULL;\r
+ subscription.callback.function = pCallbackWrapper[ type ];\r
+\r
+ /* Call the MQTT operation function. */\r
+ mqttStatus = mqttOperation( mqttConnection,\r
+ &subscription,\r
+ 1,\r
+ 0,\r
+ _AwsIotShadowMqttTimeoutMs );\r
+\r
+ /* Check the result of the MQTT operation. */\r
+ if( mqttStatus != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to %s callback for %.*s %s callback, error %s.",\r
+ mqttOperation == IotMqtt_SubscribeSync ? "subscribe to" : "unsubscribe from",\r
+ pSubscription->thingNameLength,\r
+ pSubscription->pThingName,\r
+ _pAwsIotShadowCallbackNames[ type ],\r
+ IotMqtt_strerror( mqttStatus ) );\r
+\r
+ /* Convert the MQTT "NO MEMORY" error to a Shadow "NO MEMORY" error. */\r
+ IOT_SET_AND_GOTO_CLEANUP( SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( mqttStatus ) );\r
+ }\r
+\r
+ IotLogDebug( "Successfully %s %.*s Shadow %s callback.",\r
+ mqttOperation == IotMqtt_SubscribeSync ? "subscribed to" : "unsubscribed from",\r
+ pSubscription->thingNameLength,\r
+ pSubscription->pThingName,\r
+ _pAwsIotShadowCallbackNames[ type ] );\r
+\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ /* MQTT subscribe should check the subscription topic buffer. */\r
+ if( mqttOperation == IotMqtt_SubscribeSync )\r
+ {\r
+ /* If the current subscription has no topic buffer, assign it the current\r
+ * topic buffer. */\r
+ if( pSubscription->pTopicBuffer == NULL )\r
+ {\r
+ pSubscription->pTopicBuffer = pTopicFilter;\r
+ }\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_END();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void _callbackWrapperCommon( _shadowCallbackType_t type,\r
+ IotMqttCallbackParam_t * pMessage )\r
+{\r
+ AwsIotShadowCallbackInfo_t callbackInfo = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;\r
+ AwsIotShadowCallbackParam_t callbackParam = { .callbackType = ( AwsIotShadowCallbackType_t ) 0 };\r
+ _shadowSubscription_t * pSubscription = NULL;\r
+ const char * pThingName = NULL;\r
+ size_t thingNameLength = 0;\r
+\r
+ /* Parse the Thing Name from the topic. */\r
+ if( AwsIot_ParseThingName( pMessage->u.message.info.pTopicName,\r
+ pMessage->u.message.info.topicNameLength,\r
+ &pThingName,\r
+ &thingNameLength ) == false )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Search for a matching subscription. */\r
+ IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );\r
+\r
+ pSubscription = _AwsIotShadow_FindSubscription( pThingName,\r
+ thingNameLength,\r
+ false );\r
+\r
+ if( pSubscription == NULL )\r
+ {\r
+ /* No subscription found. */\r
+ IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Ensure that a callback function is set. */\r
+ AwsIotShadow_Assert( pSubscription->callbacks[ type ].function != NULL );\r
+\r
+ /* Copy the subscription callback info, as the subscription may be modified\r
+ * when the subscriptions mutex is released. */\r
+ callbackInfo = pSubscription->callbacks[ type ];\r
+\r
+ IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );\r
+\r
+ /* Set the callback type. Shadow callbacks are enumerated after the operations. */\r
+ callbackParam.callbackType = ( AwsIotShadowCallbackType_t ) ( type + SHADOW_OPERATION_COUNT );\r
+\r
+ /* Set the remaining members of the callback param. */\r
+ callbackParam.mqttConnection = pMessage->mqttConnection;\r
+ callbackParam.pThingName = pThingName;\r
+ callbackParam.thingNameLength = thingNameLength;\r
+ callbackParam.u.callback.pDocument = pMessage->u.message.info.pPayload;\r
+ callbackParam.u.callback.documentLength = pMessage->u.message.info.payloadLength;\r
+\r
+ /* Invoke the callback function. */\r
+ callbackInfo.function( callbackInfo.pCallbackContext,\r
+ &callbackParam );\r
+\r
+ /* This function uses cleanup sections to exit on error. */\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void _deltaCallbackWrapper( void * pArgument,\r
+ IotMqttCallbackParam_t * pMessage )\r
+{\r
+ /* Silence warnings about unused parameters. */\r
+ ( void ) pArgument;\r
+\r
+ _callbackWrapperCommon( DELTA_CALLBACK, pMessage );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void _updatedCallbackWrapper( void * pArgument,\r
+ IotMqttCallbackParam_t * pMessage )\r
+{\r
+ /* Silence warnings about unused parameters. */\r
+ ( void ) pArgument;\r
+\r
+ _callbackWrapperCommon( UPDATED_CALLBACK, pMessage );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_Init( uint32_t mqttTimeoutMs )\r
+{\r
+ AwsIotShadowError_t status = AWS_IOT_SHADOW_SUCCESS;\r
+ bool listInitStatus = false;\r
+\r
+ if( _initCalled == 0 )\r
+ {\r
+ /* Initialize Shadow lists and mutexes. */\r
+ listInitStatus = AwsIot_InitLists( &_AwsIotShadowPendingOperations,\r
+ &_AwsIotShadowSubscriptions,\r
+ &_AwsIotShadowPendingOperationsMutex,\r
+ &_AwsIotShadowSubscriptionsMutex );\r
+\r
+ if( listInitStatus == false )\r
+ {\r
+ IotLogInfo( "Failed to create Shadow lists." );\r
+\r
+ status = AWS_IOT_SHADOW_INIT_FAILED;\r
+ }\r
+ else\r
+ {\r
+ /* Save the MQTT timeout option. */\r
+ if( mqttTimeoutMs != 0 )\r
+ {\r
+ _AwsIotShadowMqttTimeoutMs = mqttTimeoutMs;\r
+ }\r
+\r
+ /* Set the flag that specifies initialization is complete. */\r
+ _initCalled = 1;\r
+\r
+ IotLogInfo( "Shadow library successfully initialized." );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ IotLogWarn( "AwsIotShadow_Init called with library already initialized." );\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void AwsIotShadow_Cleanup( void )\r
+{\r
+ if( _initCalled == 1 )\r
+ {\r
+ _initCalled = 0;\r
+\r
+ /* Remove and free all items in the Shadow pending operation list. */\r
+ IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );\r
+ IotListDouble_RemoveAll( &( _AwsIotShadowPendingOperations ),\r
+ _AwsIotShadow_DestroyOperation,\r
+ offsetof( _shadowOperation_t, link ) );\r
+ IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );\r
+\r
+ /* Remove and free all items in the Shadow subscription list. */\r
+ IotMutex_Lock( &( _AwsIotShadowSubscriptionsMutex ) );\r
+ IotListDouble_RemoveAll( &( _AwsIotShadowSubscriptions ),\r
+ _AwsIotShadow_DestroySubscription,\r
+ offsetof( _shadowSubscription_t, link ) );\r
+ IotMutex_Unlock( &( _AwsIotShadowSubscriptionsMutex ) );\r
+\r
+ /* Destroy Shadow library mutexes. */\r
+ IotMutex_Destroy( &( _AwsIotShadowPendingOperationsMutex ) );\r
+ IotMutex_Destroy( &( _AwsIotShadowSubscriptionsMutex ) );\r
+\r
+ /* Restore the default MQTT timeout. */\r
+ _AwsIotShadowMqttTimeoutMs = AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS;\r
+\r
+ IotLogInfo( "Shadow library cleanup done." );\r
+ }\r
+ else\r
+ {\r
+ IotLogWarn( "AwsIotShadow_Init was not called before AwsIotShadow_Cleanup." );\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_DeleteAsync( IotMqttConnection_t mqttConnection,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ uint32_t flags,\r
+ const AwsIotShadowCallbackInfo_t * pCallbackInfo,\r
+ AwsIotShadowOperation_t * const pDeleteOperation )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );\r
+ _shadowOperation_t * pOperation = NULL;\r
+\r
+ /* Check that AwsIotShadow_Init was called. */\r
+ if( _checkInit() == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NOT_INITIALIZED );\r
+ }\r
+\r
+ /* Validate the Thing Name and flags for Shadow DELETE. */\r
+ status = _validateThingNameFlags( SHADOW_DELETE,\r
+ pThingName,\r
+ thingNameLength,\r
+ flags,\r
+ pCallbackInfo,\r
+ pDeleteOperation );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* The Thing Name or some flag was invalid. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Allocate a new Shadow operation for DELETE. */\r
+ status = _AwsIotShadow_CreateOperation( &pOperation,\r
+ SHADOW_DELETE,\r
+ flags,\r
+ pCallbackInfo );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* No memory for a new Shadow operation. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Check the members set by Shadow operation creation. */\r
+ AwsIotShadow_Assert( pOperation != NULL );\r
+ AwsIotShadow_Assert( pOperation->type == SHADOW_DELETE );\r
+ AwsIotShadow_Assert( pOperation->flags == flags );\r
+ AwsIotShadow_Assert( pOperation->status == AWS_IOT_SHADOW_STATUS_PENDING );\r
+\r
+ /* Set the reference if provided. This must be done before the Shadow operation\r
+ * is processed. */\r
+ if( pDeleteOperation != NULL )\r
+ {\r
+ *pDeleteOperation = pOperation;\r
+ }\r
+\r
+ /* Process the Shadow operation. This subscribes to any required topics and\r
+ * sends the MQTT message for the Shadow operation. */\r
+ status = _AwsIotShadow_ProcessOperation( mqttConnection,\r
+ pThingName,\r
+ thingNameLength,\r
+ pOperation,\r
+ NULL );\r
+\r
+ /* If the Shadow operation failed, clear the now invalid reference. */\r
+ if( ( status != AWS_IOT_SHADOW_STATUS_PENDING ) && ( pDeleteOperation != NULL ) )\r
+ {\r
+ *pDeleteOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_DeleteSync( IotMqttConnection_t mqttConnection,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs )\r
+{\r
+ AwsIotShadowError_t status = AWS_IOT_SHADOW_STATUS_PENDING;\r
+ AwsIotShadowOperation_t deleteOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;\r
+\r
+ /* Set the waitable flag. */\r
+ flags |= AWS_IOT_SHADOW_FLAG_WAITABLE;\r
+\r
+ /* Call the asynchronous Shadow delete function. */\r
+ status = AwsIotShadow_DeleteAsync( mqttConnection,\r
+ pThingName,\r
+ thingNameLength,\r
+ flags,\r
+ NULL,\r
+ &deleteOperation );\r
+\r
+ /* Wait for the Shadow delete operation to complete. */\r
+ if( status == AWS_IOT_SHADOW_STATUS_PENDING )\r
+ {\r
+ status = AwsIotShadow_Wait( deleteOperation, timeoutMs, NULL, NULL );\r
+ }\r
+\r
+ /* Ensure that a status was set. */\r
+ AwsIotShadow_Assert( status != AWS_IOT_SHADOW_STATUS_PENDING );\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_GetAsync( IotMqttConnection_t mqttConnection,\r
+ const AwsIotShadowDocumentInfo_t * pGetInfo,\r
+ uint32_t flags,\r
+ const AwsIotShadowCallbackInfo_t * pCallbackInfo,\r
+ AwsIotShadowOperation_t * const pGetOperation )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );\r
+ _shadowOperation_t * pOperation = NULL;\r
+\r
+ /* Check that AwsIotShadow_Init was called. */\r
+ if( _checkInit() == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NOT_INITIALIZED );\r
+ }\r
+\r
+ /* Validate the Thing Name and flags for Shadow GET. */\r
+ status = _validateThingNameFlags( SHADOW_GET,\r
+ pGetInfo->pThingName,\r
+ pGetInfo->thingNameLength,\r
+ flags,\r
+ pCallbackInfo,\r
+ pGetOperation );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* The Thing Name or some flag was invalid. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Validate the document info for Shadow GET. */\r
+ status = _validateDocumentInfo( SHADOW_GET,\r
+ flags,\r
+ pGetInfo );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* Document info was invalid. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Allocate a new Shadow operation for GET. */\r
+ status = _AwsIotShadow_CreateOperation( &pOperation,\r
+ SHADOW_GET,\r
+ flags,\r
+ pCallbackInfo );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* No memory for a new Shadow operation. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Check the members set by Shadow operation creation. */\r
+ AwsIotShadow_Assert( pOperation != NULL );\r
+ AwsIotShadow_Assert( pOperation->type == SHADOW_GET );\r
+ AwsIotShadow_Assert( pOperation->flags == flags );\r
+ AwsIotShadow_Assert( pOperation->status == AWS_IOT_SHADOW_STATUS_PENDING );\r
+\r
+ /* Copy the memory allocation function. */\r
+ pOperation->u.get.mallocDocument = pGetInfo->u.get.mallocDocument;\r
+\r
+ /* Set the reference if provided. This must be done before the Shadow operation\r
+ * is processed. */\r
+ if( pGetOperation != NULL )\r
+ {\r
+ *pGetOperation = pOperation;\r
+ }\r
+\r
+ /* Process the Shadow operation. This subscribes to any required topics and\r
+ * sends the MQTT message for the Shadow operation. */\r
+ status = _AwsIotShadow_ProcessOperation( mqttConnection,\r
+ pGetInfo->pThingName,\r
+ pGetInfo->thingNameLength,\r
+ pOperation,\r
+ pGetInfo );\r
+\r
+ /* If the Shadow operation failed, clear the now invalid reference. */\r
+ if( ( status != AWS_IOT_SHADOW_STATUS_PENDING ) && ( pGetOperation != NULL ) )\r
+ {\r
+ *pGetOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_GetSync( IotMqttConnection_t mqttConnection,\r
+ const AwsIotShadowDocumentInfo_t * pGetInfo,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs,\r
+ const char ** const pShadowDocument,\r
+ size_t * const pShadowDocumentLength )\r
+{\r
+ AwsIotShadowError_t status = AWS_IOT_SHADOW_STATUS_PENDING;\r
+ AwsIotShadowOperation_t getOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;\r
+\r
+ /* Set the waitable flag. */\r
+ flags |= AWS_IOT_SHADOW_FLAG_WAITABLE;\r
+\r
+ /* Call the asynchronous Shadow get function. */\r
+ status = AwsIotShadow_GetAsync( mqttConnection,\r
+ pGetInfo,\r
+ flags,\r
+ NULL,\r
+ &getOperation );\r
+\r
+ /* Wait for the Shadow get operation to complete. */\r
+ if( status == AWS_IOT_SHADOW_STATUS_PENDING )\r
+ {\r
+ status = AwsIotShadow_Wait( getOperation,\r
+ timeoutMs,\r
+ pShadowDocument,\r
+ pShadowDocumentLength );\r
+ }\r
+\r
+ /* Ensure that a status was set. */\r
+ AwsIotShadow_Assert( status != AWS_IOT_SHADOW_STATUS_PENDING );\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_UpdateAsync( IotMqttConnection_t mqttConnection,\r
+ const AwsIotShadowDocumentInfo_t * pUpdateInfo,\r
+ uint32_t flags,\r
+ const AwsIotShadowCallbackInfo_t * pCallbackInfo,\r
+ AwsIotShadowOperation_t * const pUpdateOperation )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );\r
+ _shadowOperation_t * pOperation = NULL;\r
+ const char * pClientToken = NULL;\r
+ size_t clientTokenLength = 0;\r
+\r
+ /* Check that AwsIotShadow_Init was called. */\r
+ if( _checkInit() == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NOT_INITIALIZED );\r
+ }\r
+\r
+ /* Validate the Thing Name and flags for Shadow UPDATE. */\r
+ status = _validateThingNameFlags( SHADOW_UPDATE,\r
+ pUpdateInfo->pThingName,\r
+ pUpdateInfo->thingNameLength,\r
+ flags,\r
+ pCallbackInfo,\r
+ pUpdateOperation );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* The Thing Name or some flag was invalid. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Validate the document info for Shadow UPDATE. */\r
+ status = _validateDocumentInfo( SHADOW_UPDATE,\r
+ flags,\r
+ pUpdateInfo );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* Document info was invalid. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Check UPDATE document for a client token. */\r
+ if( AwsIot_GetClientToken( pUpdateInfo->u.update.pUpdateDocument,\r
+ pUpdateInfo->u.update.updateDocumentLength,\r
+ &pClientToken,\r
+ &clientTokenLength ) == false )\r
+ {\r
+ IotLogError( "Shadow document for Shadow UPDATE does not contain a valid client token." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+\r
+ /* Allocate a new Shadow operation for UPDATE. */\r
+ status = _AwsIotShadow_CreateOperation( &pOperation,\r
+ SHADOW_UPDATE,\r
+ flags,\r
+ pCallbackInfo );\r
+\r
+ if( status != AWS_IOT_SHADOW_SUCCESS )\r
+ {\r
+ /* No memory for a new Shadow operation. */\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Check the members set by Shadow operation creation. */\r
+ AwsIotShadow_Assert( pOperation != NULL );\r
+ AwsIotShadow_Assert( pOperation->type == SHADOW_UPDATE );\r
+ AwsIotShadow_Assert( pOperation->flags == flags );\r
+ AwsIotShadow_Assert( pOperation->status == AWS_IOT_SHADOW_STATUS_PENDING );\r
+\r
+ /* Allocate memory for the client token. */\r
+ pOperation->u.update.pClientToken = AwsIotShadow_MallocString( clientTokenLength );\r
+\r
+ if( pOperation->u.update.pClientToken == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for Shadow update client token." );\r
+ _AwsIotShadow_DestroyOperation( pOperation );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );\r
+ }\r
+\r
+ /* Copy the client token. The client token must be copied in case the application\r
+ * frees the buffer containing it. */\r
+ ( void ) memcpy( ( void * ) pOperation->u.update.pClientToken,\r
+ pClientToken,\r
+ clientTokenLength );\r
+ pOperation->u.update.clientTokenLength = clientTokenLength;\r
+\r
+ /* Set the reference if provided. This must be done before the Shadow operation\r
+ * is processed. */\r
+ if( pUpdateOperation != NULL )\r
+ {\r
+ *pUpdateOperation = pOperation;\r
+ }\r
+\r
+ /* Process the Shadow operation. This subscribes to any required topics and\r
+ * sends the MQTT message for the Shadow operation. */\r
+ status = _AwsIotShadow_ProcessOperation( mqttConnection,\r
+ pUpdateInfo->pThingName,\r
+ pUpdateInfo->thingNameLength,\r
+ pOperation,\r
+ pUpdateInfo );\r
+\r
+ /* If the Shadow operation failed, clear the now invalid reference. */\r
+ if( ( status != AWS_IOT_SHADOW_STATUS_PENDING ) && ( pUpdateOperation != NULL ) )\r
+ {\r
+ *pUpdateOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_UpdateSync( IotMqttConnection_t mqttConnection,\r
+ const AwsIotShadowDocumentInfo_t * pUpdateInfo,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs )\r
+{\r
+ AwsIotShadowError_t status = AWS_IOT_SHADOW_STATUS_PENDING;\r
+ AwsIotShadowOperation_t updateOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;\r
+\r
+ /* Set the waitable flag. */\r
+ flags |= AWS_IOT_SHADOW_FLAG_WAITABLE;\r
+\r
+ /* Call the asynchronous Shadow update function. */\r
+ status = AwsIotShadow_UpdateAsync( mqttConnection,\r
+ pUpdateInfo,\r
+ flags,\r
+ NULL,\r
+ &updateOperation );\r
+\r
+ /* Wait for the Shadow update operation to complete. */\r
+ if( status == AWS_IOT_SHADOW_STATUS_PENDING )\r
+ {\r
+ status = AwsIotShadow_Wait( updateOperation, timeoutMs, NULL, NULL );\r
+ }\r
+\r
+ /* Ensure that a status was set. */\r
+ AwsIotShadow_Assert( status != AWS_IOT_SHADOW_STATUS_PENDING );\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_Wait( AwsIotShadowOperation_t operation,\r
+ uint32_t timeoutMs,\r
+ const char ** const pShadowDocument,\r
+ size_t * const pShadowDocumentLength )\r
+{\r
+ IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );\r
+\r
+ /* Check that AwsIotShadow_Init was called. */\r
+ if( _checkInit() == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NOT_INITIALIZED );\r
+ }\r
+\r
+ /* Check that reference is set. */\r
+ if( operation == NULL )\r
+ {\r
+ IotLogError( "Operation reference cannot be NULL." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+\r
+ /* Check that reference is waitable. */\r
+ if( ( operation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == 0 )\r
+ {\r
+ IotLogError( "Operation is not waitable." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+\r
+ /* Check that output parameters are set for a Shadow GET. */\r
+ if( operation->type == SHADOW_GET )\r
+ {\r
+ if( ( pShadowDocument == NULL ) || ( pShadowDocumentLength == NULL ) )\r
+ {\r
+ IotLogError( "Output buffer and size pointer must be set for Shadow GET." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_PARAMETER );\r
+ }\r
+ }\r
+\r
+ /* Wait for a response to the Shadow operation. */\r
+ if( IotSemaphore_TimedWait( &( operation->notify.waitSemaphore ),\r
+ timeoutMs ) == true )\r
+ {\r
+ status = operation->status;\r
+ }\r
+ else\r
+ {\r
+ status = AWS_IOT_SHADOW_TIMEOUT;\r
+ }\r
+\r
+ /* Remove the completed operation from the pending operation list. */\r
+ IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );\r
+ IotListDouble_Remove( &( operation->link ) );\r
+ IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );\r
+\r
+ /* Decrement the reference count. This also removes subscriptions if the\r
+ * count reaches 0. */\r
+ IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );\r
+ _AwsIotShadow_DecrementReferences( operation,\r
+ operation->pSubscription->pTopicBuffer,\r
+ NULL );\r
+ IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );\r
+\r
+ /* Set the output parameters for Shadow GET. */\r
+ if( ( operation->type == SHADOW_GET ) &&\r
+ ( status == AWS_IOT_SHADOW_SUCCESS ) )\r
+ {\r
+ *pShadowDocument = operation->u.get.pDocument;\r
+ *pShadowDocumentLength = operation->u.get.documentLength;\r
+ }\r
+\r
+ /* Destroy the Shadow operation. */\r
+ _AwsIotShadow_DestroyOperation( operation );\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_SetDeltaCallback( IotMqttConnection_t mqttConnection,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ uint32_t flags,\r
+ const AwsIotShadowCallbackInfo_t * pDeltaCallback )\r
+{\r
+ /* Flags are currently not used by this function. */\r
+ ( void ) flags;\r
+\r
+ return _setCallbackCommon( mqttConnection,\r
+ DELTA_CALLBACK,\r
+ pThingName,\r
+ thingNameLength,\r
+ pDeltaCallback );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+AwsIotShadowError_t AwsIotShadow_SetUpdatedCallback( IotMqttConnection_t mqttConnection,\r
+ const char * pThingName,\r
+ size_t thingNameLength,\r
+ uint32_t flags,\r
+ const AwsIotShadowCallbackInfo_t * pUpdatedCallback )\r
+{\r
+ /* Flags are currently not used by this function. */\r
+ ( void ) flags;\r
+\r
+ return _setCallbackCommon( mqttConnection,\r
+ UPDATED_CALLBACK,\r
+ pThingName,\r
+ thingNameLength,\r
+ pUpdatedCallback );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+const char * AwsIotShadow_strerror( AwsIotShadowError_t status )\r
+{\r
+ const char * pMessage = NULL;\r
+\r
+ switch( status )\r
+ {\r
+ case AWS_IOT_SHADOW_SUCCESS:\r
+ pMessage = "SUCCESS";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_STATUS_PENDING:\r
+ pMessage = "STATUS PENDING";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_INIT_FAILED:\r
+ pMessage = "INITIALIZATION FAILED";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_BAD_PARAMETER:\r
+ pMessage = "BAD PARAMETER";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_NO_MEMORY:\r
+ pMessage = "NO MEMORY";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_MQTT_ERROR:\r
+ pMessage = "MQTT LIBRARY ERROR";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_BAD_RESPONSE:\r
+ pMessage = "BAD RESPONSE RECEIVED";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_TIMEOUT:\r
+ pMessage = "TIMEOUT";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_NOT_INITIALIZED:\r
+ pMessage = "NOT INITIALIZED";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_BAD_REQUEST:\r
+ pMessage = "REJECTED: 400 BAD REQUEST";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_UNAUTHORIZED:\r
+ pMessage = "REJECTED: 401 UNAUTHORIZED";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_FORBIDDEN:\r
+ pMessage = "REJECTED: 403 FORBIDDEN";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_NOT_FOUND:\r
+ pMessage = "REJECTED: 404 NOT FOUND";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_CONFLICT:\r
+ pMessage = "REJECTED: 409 VERSION CONFLICT";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_TOO_LARGE:\r
+ pMessage = "REJECTED: 413 PAYLOAD TOO LARGE";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_UNSUPPORTED:\r
+ pMessage = "REJECTED: 415 UNSUPPORTED ENCODING";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_TOO_MANY_REQUESTS:\r
+ pMessage = "REJECTED: 429 TOO MANY REQUESTS";\r
+ break;\r
+\r
+ case AWS_IOT_SHADOW_SERVER_ERROR:\r
+ pMessage = "500 SERVER ERROR";\r
+ break;\r
+\r
+ default:\r
+ pMessage = "INVALID STATUS";\r
+ break;\r
+ }\r
+\r
+ return pMessage;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r