]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/shadow/src/aws_iot_shadow_api.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / aws / shadow / src / aws_iot_shadow_api.c
diff --git a/FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/shadow/src/aws_iot_shadow_api.c b/FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/shadow/src/aws_iot_shadow_api.c
new file mode 100644 (file)
index 0000000..b9936bb
--- /dev/null
@@ -0,0 +1,1333 @@
+/*\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