diff --git a/.github/.cSpellWords.txt b/.github/.cSpellWords.txt index 71edb8704..4e2d4c5a4 100644 --- a/.github/.cSpellWords.txt +++ b/.github/.cSpellWords.txt @@ -52,3 +52,10 @@ Werror Wextra Wsign Wunused +mqttpropadd +mqttpropget +pubsubscriptionid +mqttpropertybuilder +MQTTV +subscribeid + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f24f81a48..75d306de3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: uses: FreeRTOS/CI-CD-Github-Actions/complexity@main with: path: ./ - horrid_threshold: 12 + horrid_threshold: 17 doxygen: runs-on: ubuntu-latest diff --git a/CTestTestfile.cmake b/CTestTestfile.cmake new file mode 100644 index 000000000..e4ee2c7a3 --- /dev/null +++ b/CTestTestfile.cmake @@ -0,0 +1,7 @@ +# CMake generated Testfile for +# Source directory: /home/ubuntu/coreMQTT/test +# Build directory: /home/ubuntu/coreMQTT +# +# This file includes the relevant testing commands required for +# testing this directory and lists subdirectories to be tested as well. +subdirs("unit-test") diff --git a/MISRA.md b/MISRA.md index e703c752e..87bee2d1e 100644 --- a/MISRA.md +++ b/MISRA.md @@ -37,3 +37,8 @@ _Ref 18.2.1_ to be subtracted from the index. It is manually verified that the index will always be within bounds of the array. However, Coverity is flagging this as a deviation. Thus, we are suppressing it. + +#### Rule 10.5 + +_Ref 10.5.1_ +- MISRA C-2012 Rule 10.5 states that the value of an expression should not be cast to an inappropriate essential type. In this library, reason codes are input as bytes and processed accordingly. A byte representing the reason code is cast to an enumerated type that is also based on uint8_t. It is verified that the values will always fall within the valid range of the enumeration, and the underlying type of the enumeration is compatible. Thus, this cast is considered safe. \ No newline at end of file diff --git a/MigrationGuide.md b/MigrationGuide.md index 2a2aada90..e085a96a6 100644 --- a/MigrationGuide.md +++ b/MigrationGuide.md @@ -1,6 +1,6 @@ -## coreMQTT version >=v2.0.0 Migration Guide +## coreMQTT version v2.x Migration Guide -With coreMQTT versions >=v2.0.0, there are some breaking changes that need to be addressed when upgrading. +With coreMQTT versions >=v2.x, there are some breaking changes that need to be addressed when upgrading. ### Breaking Changes @@ -232,3 +232,1647 @@ if( status == MQTTSuccess ) ### Additional Changes * The `MQTT_CancelCallback` function has been added to allow a program to prevent the event callback from being called when receiving an ACK for a sent packet. For example, if a program sends a publish with packet ID 2 and QoS > 0 using `MQTT_Publish`, the program could then call `MQTT_CancelCallback` on packet ID 2 to prevent coreMQTT from calling the event callback when it receives the `PUBACK` for packet ID 2. + +## coreMQTT version >=v3.0.0 Migration Guide + +With coreMQTT versions >=v3.0.0, there are some breaking changes that need to be addressed when upgrading. + +### Breaking Changes + +* The `MQTTEventCallback_t` function signature used in `MQTT_Init` has changed to support MQTT v5 properties and reason codes. The signature changed from `void (* MQTTEventCallback_t)( struct MQTTContext * pContext, struct MQTTPacketInfo * pPacketInfo, struct MQTTDeserializedInfo * pDeserializedInfo )` to `void (* MQTTEventCallback_t)( struct MQTTContext * pContext, struct MQTTPacketInfo * pPacketInfo, struct MQTTDeserializedInfo * pDeserializedInfo, enum MQTTSuccessFailReasonCode * pReasonCode, struct MQTTPropBuilder * sendPropsBuffer, struct MQTTPropBuilder * getPropsBuffer )`. For example: + +**Old Code Snippet**: +``` +// Callback function implementation +static void eventCallback( + MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo ) +{ + /* Handle incoming publish. The lower 4 bits of the publish packet + * type is used for the dup, QoS, and retain flags. Hence masking + * out the lower bits to check if the packet is publish. */ + if( ( pPacketInfo->type & 0xF0U ) == MQTT_PACKET_TYPE_PUBLISH ) + { + /* Handle incoming publish. */ + } + else + { + /* Handle other packets. */ + } +} + +// MQTT_Init call +status = MQTT_Init( &mqttContext, + &transport, + getTimeMs, + eventCallback, + &fixedBuffer ); +``` +**New Code Snippet**: +``` +// Callback function implementation +static void eventCallback( + MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode * pReasonCode, + MQTTPropBuilder_t * sendPropsBuffer, + MQTTPropBuilder_t * getPropsBuffer ) +{ + /* Handle incoming publish. The lower 4 bits of the publish packet + * type is used for the dup, QoS, and retain flags. Hence masking + * out the lower bits to check if the packet is publish. */ + if( ( pPacketInfo->type & 0xF0U ) == MQTT_PACKET_TYPE_PUBLISH ) + { + /* Handle incoming publish. */ + + /* Access publish properties if needed */ + uint16_t topicAlias; + if( MQTTPropGet_PubTopicAlias( getPropsBuffer, &topicAlias ) == MQTTSuccess ) + { + /* Handle topic alias */ + } + } + else + { + /* Handle other packets. */ + + /* Add properties to outgoing ack packets if needed */ + if( pPacketInfo->type == MQTT_PACKET_TYPE_PUBACK ) + { + MQTTPropAdd_ReasonString( sendPropsBuffer, "Success", 7 ); + } + } +} + +// MQTT_Init call +status = MQTT_Init( &mqttContext, + &transport, + getTimeMs, + eventCallback, + &fixedBuffer ); +``` +* The `MQTT_InitStatefulQoS` function now includes support for MQTT v5 properties in outgoing publish acknowledgments with two additional parameters. Thus, the signature of `MQTT_InitStatefulQoS` changed from `MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext, MQTTPubAckInfo_t * pOutgoingPublishRecords, size_t outgoingPublishCount, MQTTPubAckInfo_t * pIncomingPublishRecords, size_t incomingPublishCount )` to `MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext, MQTTPubAckInfo_t * pOutgoingPublishRecords, size_t outgoingPublishCount, MQTTPubAckInfo_t * pIncomingPublishRecords, size_t incomingPublishCount, uint8_t * pBuffer, size_t bufferLength )`. The new parameters can be set to NULL and 0 respectively if not using MQTT v5 properties in publish acknowledgments. For example: + +**Old Code Snippet**: +``` +// Variables used in this example. +MQTTContext_t mqttContext; +const size_t outgoingPublishCount = 10; +const size_t incomingPublishCount = 10; +MQTTPubAckInfo_t pOutgoingPublishRecords[ outgoingPublishCount ]; +MQTTPubAckInfo_t pIncomingPublishRecords[ incomingPublishCount ]; + +status = MQTT_InitStatefulQoS( &mqttContext, + pOutgoingPublishRecords, + outgoingPublishCount, + pIncomingPublishRecords, + incomingPublishCount ); + +if( status == MQTTSuccess ) +{ + // QoS > 0 operations can now be performed +} +``` +**New Code snippet**: +``` +/ Variables used in this example. +MQTTContext_t mqttContext; +const size_t outgoingPublishCount = 10; +const size_t incomingPublishCount = 10; +MQTTPubAckInfo_t pOutgoingPublishRecords[ outgoingPublishCount ]; +MQTTPubAckInfo_t pIncomingPublishRecords[ incomingPublishCount ]; + +// Option 1: Without MQTT v5 properties in publish acknowledgments +status = MQTT_InitStatefulQoS( &mqttContext, + pOutgoingPublishRecords, + outgoingPublishCount, + pIncomingPublishRecords, + incomingPublishCount, + NULL, // No buffer for properties + 0 ); // No buffer length + +// Option 2: With MQTT v5 properties in publish acknowledgments +uint8_t propertyBuffer[500]; +status = MQTT_InitStatefulQoS( &mqttContext, + pOutgoingPublishRecords, + outgoingPublishCount, + pIncomingPublishRecords, + incomingPublishCount, + propertyBuffer, // Buffer for properties + sizeof(propertyBuffer) ); + +if( status == MQTTSuccess ) +{ + // QoS > 0 operations can now be performed +} +``` +* The `MQTT_Connect` function now includes MQTT v5 property support with two additional parameters. Thus, the signature of `MQTT_Connect` changed from `MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, uint32_t timeoutMs, bool * pSessionPresent )` to `MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, uint32_t timeoutMs, bool * pSessionPresent, const MQTTPropBuilder_t * pPropertyBuilder, const MQTTPropBuilder_t * pWillPropertyBuilder )`.`pPropertyBuilder` and `pWillPropertyBuilder` can be set to NULL if the user does not want to send any properties. + +**Old Code Snippet**: +``` +// Variables used in this example. +MQTTStatus_t status; +MQTTConnectInfo_t connectInfo = { 0 }; +bool sessionPresent; +// This context is assumed to be initialized. +MQTTContext_t * pContext; + +// Set connection info. +connectInfo.keepAliveSeconds = 60; +connectInfo.cleanSession = true; +connectInfo.pClientIdentifier = "clientId"; +connectInfo.clientIdentifierLength = strlen("clientId"); + +status = MQTT_Connect( pContext, + &connectInfo, + NULL, /* No will message */ + MQTT_TIMEOUT_MS, + &sessionPresent ); + +if( status == MQTTSuccess ) +{ + // Connection successful +} +``` +**New Code Snippet**: +``` +// Variables used in this example. +MQTTStatus_t status; +MQTTConnectInfo_t connectInfo = { 0 }; +bool sessionPresent; +// This context is assumed to be initialized. +MQTTContext_t * pContext; + +// Set connection info. +connectInfo.keepAliveSeconds = 60; +connectInfo.cleanSession = true; +connectInfo.pClientIdentifier = "clientId"; +connectInfo.clientIdentifierLength = strlen("clientId"); + +status = MQTT_Connect( pContext, + &connectInfo, + NULL, /* No will message */ + MQTT_TIMEOUT_MS, + &sessionPresent, + NULL, /* No connect properties */ + NULL /* No will properties */ ); + +if( status == MQTTSuccess ) +{ + // Connection successful +} + +// To use MQTT v5 properties, initialize and use the property builders: + +// Variables used in this example. +MQTTStatus_t status; +MQTTConnectInfo_t connectInfo = { 0 }; +bool sessionPresent; +MQTTPropBuilder_t connectProperties = { 0 }; +// This context is assumed to be initialized. +MQTTContext_t * pContext; + +// Set connection info. +connectInfo.keepAliveSeconds = 60; +connectInfo.cleanSession = true; +connectInfo.pClientIdentifier = "clientId"; +connectInfo.clientIdentifierLength = strlen("clientId"); + +// Initialize and add connect properties +MQTTPropBuilder_t connectProperties ; +uint8_t buf[500] ; +size bufLength = sizeof(buf); +MQTT_PropertyBuilder_Init(&connectProperties, buf, bufLength) ; + +uint32_t sessionExpiryInterval = 100 ; // 100ms +MQTTPropAdd_SessionExpiry(&connectProperties, sessionExpiryInterval ); + +// Can also use the will properties in a similar way. + +status = MQTT_Connect( pContext, + &connectInfo, + NULL, /* No will message */ + MQTT_TIMEOUT_MS, + &sessionPresent, + &connectProperties, + NULL /* No will properties */ ); +if( status == MQTTSuccess ) +{ + // Connection successful +} +``` +* The `MQTT_Subscribe` function now includes support for MQTT v5 properties with an additional parameter. Thus, the signature of `MQTT_Subscribe` changed from `MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId )` to `MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId, const MQTTPropBuilder_t * pPropertyBuilder )`. The new parameter can be set to NULL if not using MQTT v5 properties. For example: + +**Old Code Snippet**: +``` +// Variables used in this example. +MQTTContext_t mqttContext; +MQTTSubscribeInfo_t subscriptionList[1]; +uint16_t packetId; + +// Configure subscription +subscriptionList[0].qos = MQTTQoS0; +subscriptionList[0].pTopicFilter = "topic/example"; +subscriptionList[0].topicFilterLength = strlen("topic/example"); + +// Get packet id +packetId = MQTT_GetPacketId(&mqttContext); + +status = MQTT_Subscribe(&mqttContext, + subscriptionList, + 1, + packetId); + +if(status == MQTTSuccess) +{ + // Subscription request sent successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTContext_t mqttContext; +MQTTSubscribeInfo_t subscriptionList[1]; +uint16_t packetId; + +// Configure subscription +subscriptionList[0].qos = MQTTQoS0; +subscriptionList[0].pTopicFilter = "topic/example"; +subscriptionList[0].topicFilterLength = strlen("topic/example"); + +// Get packet id +packetId = MQTT_GetPacketId(&mqttContext); + +// Option 1: Without MQTT v5 properties +status = MQTT_Subscribe(&mqttContext, + subscriptionList, + 1, + packetId, + NULL); // No properties + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t propertyBuilder = { 0 }; +uint8_t propertyBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&propertyBuilder, + propertyBuffer, + sizeof(propertyBuffer)); + +// Add subscription identifier property +MQTTPropAdd_SubscribeId(&propertyBuilder, 1); + +status = MQTT_Subscribe(&mqttContext, + subscriptionList, + 1, + packetId, + &propertyBuilder); + +if(status == MQTTSuccess) +{ + // Subscription request sent successfully +} +``` +* The `MQTT_Publish` function now includes support for MQTT v5 properties with an additional parameter. Thus, the signature of `MQTT_Publish` changed from `MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, uint16_t packetId )` to `MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, uint16_t packetId, const MQTTPropBuilder_t * pPropertyBuilder )`. The new parameter can be set to NULL if not using MQTT v5 properties. For example: + +**Old Code Snippet**: +``` +// Variables used in this example. +MQTTContext_t mqttContext; +MQTTPublishInfo_t publishInfo = { 0 }; +uint16_t packetId; + +// Configure publish message +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +// Get packet id for QoS > 0 +packetId = MQTT_GetPacketId(&mqttContext); + +status = MQTT_Publish(&mqttContext, + &publishInfo, + packetId); + +if(status == MQTTSuccess) +{ + // Message published successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTContext_t mqttContext; +MQTTPublishInfo_t publishInfo = { 0 }; +uint16_t packetId; + +// Configure publish message +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +// Get packet id for QoS > 0 +packetId = MQTT_GetPacketId(&mqttContext); + +// Option 1: Without MQTT v5 properties +status = MQTT_Publish(&mqttContext, + &publishInfo, + packetId, + NULL); // No properties + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t propertyBuilder = { 0 }; +uint8_t propertyBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&propertyBuilder, + propertyBuffer, + sizeof(propertyBuffer)); + +// Add publish properties +MQTTPropAdd_PubPayloadFormat(&propertyBuilder, 1); +MQTTPropAdd_PubTopicAlias(&propertyBuilder, 1); + +status = MQTT_Publish(&mqttContext, + &publishInfo, + packetId, + &propertyBuilder); + +if(status == MQTTSuccess) +{ + // Message published successfully +} +``` +* The `MQTT_Unsubscribe` function now includes support for MQTT v5 properties with an additional parameter. Thus, the signature of `MQTT_Unsubscribe` changed from `MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId )` to `MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId, const MQTTPropBuilder_t * pPropertyBuilder )`. The new parameter can be set to NULL if not using MQTT v5 properties. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTContext_t mqttContext; +MQTTSubscribeInfo_t unsubscribeList[1]; +uint16_t packetId; + +// Configure unsubscribe info +unsubscribeList[0].pTopicFilter = "topic/example"; +unsubscribeList[0].topicFilterLength = strlen("topic/example"); +// QoS field is unused for unsubscribe + +// Get packet id +packetId = MQTT_GetPacketId(&mqttContext); + +status = MQTT_Unsubscribe(&mqttContext, + unsubscribeList, + 1, + packetId); + +if(status == MQTTSuccess) +{ + // Unsubscribe request sent successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTContext_t mqttContext; +MQTTSubscribeInfo_t unsubscribeList[1]; +uint16_t packetId; + +// Configure unsubscribe info +unsubscribeList[0].pTopicFilter = "topic/example"; +unsubscribeList[0].topicFilterLength = strlen("topic/example"); +// QoS field is unused for unsubscribe + +// Get packet id +packetId = MQTT_GetPacketId(&mqttContext); + +// Option 1: Without MQTT v5 properties +status = MQTT_Unsubscribe(&mqttContext, + unsubscribeList, + 1, + packetId, + NULL); // No properties + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t propertyBuilder = { 0 }; +uint8_t propertyBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&propertyBuilder, + propertyBuffer, + sizeof(propertyBuffer)); + +// Add user property +MQTTUserProperty_t userProperty = { + .pKey = "key", + .keyLength = strlen("key"), + .pValue = "value", + .valueLength = strlen("value") +}; +MQTTPropAdd_UserProp(&propertyBuilder, &userProperty); + +status = MQTT_Unsubscribe(&mqttContext, + unsubscribeList, + 1, + packetId, + &propertyBuilder); + +if(status == MQTTSuccess) +{ + // Unsubscribe request sent successfully +} +``` +* The `MQTT_Disconnect` function now includes support for MQTT v5 properties and reason codes with two additional parameters. Thus, the signature of `MQTT_Disconnect` changed from `MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext )` to `MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext, const MQTTPropBuilder_t * pPropertyBuilder, MQTTSuccessFailReasonCode_t reasonCode )`. The new parameters can be set to NULL and 0 respectively if not using MQTT v5 features. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTContext_t mqttContext; + +status = MQTT_Disconnect(&mqttContext); + +if(status == MQTTSuccess) +{ + // Disconnected successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTContext_t mqttContext; + +// Option 1: Without MQTT v5 properties and reason code +status = MQTT_Disconnect(&mqttContext, + NULL, // No properties + 0); // No reason code + +// Option 2: With MQTT v5 properties and reason code +MQTTPropBuilder_t propertyBuilder = { 0 }; +uint8_t propertyBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&propertyBuilder, + propertyBuffer, + sizeof(propertyBuffer)); + +// Add disconnect properties +MQTTPropAdd_ReasonString(&propertyBuilder, + "Normal shutdown", + strlen("Normal shutdown")); + +status = MQTT_Disconnect(&mqttContext, + &propertyBuilder, + MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION); + +if(status == MQTTSuccess) +{ + // Disconnected successfully +} +``` +* The `MQTT_GetConnectPacketSize` function now includes support for MQTT v5 properties with two additional parameters. Thus, the signature of `MQTT_GetConnectPacketSize` changed from `MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, size_t * pRemainingLength, size_t * pPacketSize )` to `MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, const MQTTPropBuilder_t *pConnectProperties, const MQTTPropBuilder_t *pWillProperties, size_t * pRemainingLength, size_t * pPacketSize )`. The new parameters can be set to NULL if not using MQTT v5 properties. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTConnectInfo_t connectInfo = { 0 }; +MQTTPublishInfo_t willInfo = { 0 }; +size_t remainingLength = 0; +size_t packetSize = 0; + +// Configure connect and will info +connectInfo.keepAliveSeconds = 60; +connectInfo.cleanSession = true; +connectInfo.pClientIdentifier = "clientId"; +connectInfo.clientIdentifierLength = strlen("clientId"); + +status = MQTT_GetConnectPacketSize(&connectInfo, + &willInfo, + &remainingLength, + &packetSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTConnectInfo_t connectInfo = { 0 }; +MQTTPublishInfo_t willInfo = { 0 }; +size_t remainingLength = 0; +size_t packetSize = 0; + +// Configure connect and will info +connectInfo.keepAliveSeconds = 60; +connectInfo.cleanSession = true; +connectInfo.pClientIdentifier = "clientId"; +connectInfo.clientIdentifierLength = strlen("clientId"); + +// Option 1: Without MQTT v5 properties +status = MQTT_GetConnectPacketSize(&connectInfo, + &willInfo, + NULL, // No connect properties + NULL, // No will properties + &remainingLength, + &packetSize); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t connectProperties = { 0 }; +MQTTPropBuilder_t willProperties = { 0 }; +uint8_t connectPropBuffer[100]; +uint8_t willPropBuffer[100]; + +// Initialize property builders +MQTT_PropertyBuilder_Init(&connectProperties, + connectPropBuffer, + sizeof(connectPropBuffer)); +MQTT_PropertyBuilder_Init(&willProperties, + willPropBuffer, + sizeof(willPropBuffer)); + +// Add properties as needed +MQTTPropAdd_SessionExpiry(&connectProperties, 3600); +MQTTPropAdd_WillDelayInterval(&willProperties, 60); + +status = MQTT_GetConnectPacketSize(&connectInfo, + &willInfo, + &connectProperties, + &willProperties, + &remainingLength, + &packetSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +* The `MQTT_GetPublishPacketSize` function now includes support for MQTT v5 properties and maximum packet size with two additional parameters. Thus, the signature of `MQTT_GetPublishPacketSize` changed from `MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, size_t * pRemainingLength, size_t * pPacketSize )` to `MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, const MQTTPropBuilder_t * pPublishProperties, size_t * pRemainingLength, size_t * pPacketSize, uint32_t maxPacketSize )`. The new parameters include support for MQTT v5 properties and server-imposed packet size limitations. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTPublishInfo_t publishInfo = { 0 }; +size_t remainingLength = 0; +size_t packetSize = 0; + +// Configure publish info +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +status = MQTT_GetPublishPacketSize(&publishInfo, + &remainingLength, + &packetSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTPublishInfo_t publishInfo = { 0 }; +size_t remainingLength = 0; +size_t packetSize = 0; +uint32_t maxPacketSize = 268435455; // Default max packet size + +// Configure publish info +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +// Option 1: Without MQTT v5 properties +status = MQTT_GetPublishPacketSize(&publishInfo, + NULL, // No publish properties + &remainingLength, + &packetSize, + maxPacketSize); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t publishProperties = { 0 }; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&publishProperties, + propBuffer, + sizeof(propBuffer)); + +// Add publish properties +MQTTPropAdd_PubTopicAlias(&publishProperties, 1); +MQTTPropAdd_PubPayloadFormat(&publishProperties, 1); + +// Get max packet size from CONNACK properties +uint32_t serverMaxPacketSize = pContext->connectProperties.serverMaxPacketSize; // Value from server + +status = MQTT_GetPublishPacketSize(&publishInfo, + &publishProperties, + &remainingLength, + &packetSize, + serverMaxPacketSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +* The `MQTT_GetSubscribePacketSize` function now includes support for MQTT v5 properties and maximum packet size with two additional parameters. Thus, the signature of `MQTT_GetSubscribePacketSize` changed from `MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, size_t * pRemainingLength, size_t * pPacketSize )` to `MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, const MQTTPropBuilder_t * pSubscribeProperties, size_t * pRemainingLength, size_t * pPacketSize, uint32_t maxPacketSize )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[2]; +size_t remainingLength = 0; +size_t packetSize = 0; + +// Configure subscription list +subscriptionList[0].qos = MQTTQoS1; +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); + +subscriptionList[1].qos = MQTTQoS0; +subscriptionList[1].pTopicFilter = "topic/2"; +subscriptionList[1].topicFilterLength = strlen("topic/2"); + +status = MQTT_GetSubscribePacketSize(subscriptionList, + 2, + &remainingLength, + &packetSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[1]; +size_t remainingLength = 0; +size_t packetSize = 0; + +// Configure subscription list +subscriptionList[0].qos = MQTTQoS1; +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); +subscriptionList[0].noLocalOption = false; +subscriptionList[0].retainAsPublishedOption = false; +subscriptionList[0].retainHandlingOption = retainSendOnSub; + +MQTTPropBuilder_t subscribeProperties = { 0 }; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&subscribeProperties, + propBuffer, + sizeof(propBuffer)); + +// Add subscription identifier +MQTTPropAdd_SubscribeId(&subscribeProperties, 1); + +// Get max packet size from CONNACK properties +uint32_t serverMaxPacketSize = pContext->connectProperties.serverMaxPacketSize; // value from server + +status = MQTT_GetSubscribePacketSize(subscriptionList, + 1, + &subscribeProperties, + &remainingLength, + &packetSize, + serverMaxPacketSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +* The `MQTT_GetUnsubscribePacketSize` function now includes support for MQTT v5 properties with two additional parameters. Thus, the signature of `MQTT_GetUnsubscribePacketSize` changed from `MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, size_t * pRemainingLength, size_t * pPacketSize )` to `MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, const MQTTPropBuilder_t * pUnsubscribeProperties, size_t * pRemainingLength, size_t * pPacketSize, uint32_t maxPacketSize )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[1]; +size_t remainingLength = 0; +size_t packetSize = 0; + +// Configure unsubscribe list +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); + +status = MQTT_GetUnsubscribePacketSize(subscriptionList, + 1, + &remainingLength, + &packetSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[1]; +size_t remainingLength = 0; +size_t packetSize = 0; +uint32_t maxPacketSize = 268435455; // Default max packet size + +// Configure unsubscribe list +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); + +// Option 1: Without MQTT v5 properties +status = MQTT_GetUnsubscribePacketSize(subscriptionList, + 1, + NULL, // No unsubscribe properties + &remainingLength, + &packetSize, + maxPacketSize); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t unsubscribeProperties; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&unsubscribeProperties, + propBuffer, + sizeof(propBuffer)); + +// Add user property +MQTTUserProperty_t userProperty = { + .pKey = "key", + .keyLength = strlen("key"), + .pValue = "value", + .valueLength = strlen("value") +}; +MQTTPropAdd_UserProp(&unsubscribeProperties, &userProperty); + +// Get max packet size from CONNACK properties +uint32_t serverMaxPacketSize = pContext->connectProperties.serverMaxPacketSize; // Value from server + +status = MQTT_GetUnsubscribePacketSize(subscriptionList, + 2, + &unsubscribeProperties, + &remainingLength, + &packetSize, + serverMaxPacketSize); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +* The `MQTT_GetDisconnectPacketSize` function now includes support for MQTT v5 properties and reason code validation with four additional parameters. Thus, the signature of `MQTT_GetDisconnectPacketSize` changed from `MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize )` to `MQTTStatus_t MQTT_GetDisconnectPacketSize( const MQTTPropBuilder_t * pDisconnectProperties, size_t * pRemainingLength, size_t * pPacketSize, uint32_t maxPacketSize, MQTTSuccessFailReasonCode_t reasonCode )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +size_t packetSize = 0; + +status = MQTT_GetDisconnectPacketSize(&packetSize); + +if(status == MQTTSuccess) +{ + // Packet Size was always 2. +} +``` +**New Code Snippet** +``` +// Variables used in this example. +size_t remainingLength = 0; +size_t packetSize = 0; +uint32_t serverMaxPacketSize = pContext->connectProperties.serverMaxPacketSize ; + +// Option 1: Without MQTT v5 properties and default reason code +status = MQTT_GetDisconnectPacketSize(NULL, // No disconnect properties + &remainingLength, + &packetSize, + serverMaxPacketSize, + 0); // Default reason code + +// Option 2: With MQTT v5 properties and specific reason code +MQTTPropBuilder_t disconnectProperties; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&disconnectProperties, + propBuffer, + sizeof(propBuffer)); + +// Add disconnect properties +MQTTPropAdd_SessionExpiry(&disconnectProperties, 0); +MQTTPropAdd_ReasonString(&disconnectProperties, + "Normal shutdown", + strlen("Normal shutdown")); + + +status = MQTT_GetDisconnectPacketSize(&disconnectProperties, + &remainingLength, + &packetSize, + serverMaxPacketSize, + MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION); + +if(status == MQTTSuccess) +{ + // Use packet size information +} +``` +* The `MQTT_SerializeConnect` function now includes support for MQTT v5 properties with two additional parameters. Thus, the signature of `MQTT_SerializeConnect` changed from `MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )` to `MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, const MQTTPropBuilder_t * pConnectProperties, const MQTTPropBuilder_t * pWillProperties, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTConnectInfo_t connectInfo = { 0 }; +MQTTPublishInfo_t willInfo = { 0 }; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; + +// Configure connect info +connectInfo.keepAliveSeconds = 60; +connectInfo.cleanSession = true; +connectInfo.pClientIdentifier = "clientId"; +connectInfo.clientIdentifierLength = strlen("clientId"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Get remaining length first +status = MQTT_GetConnectPacketSize(&connectInfo, + &willInfo, + &remainingLength, + &packetSize); + +// Serialize connect packet +status = MQTT_SerializeConnect(&connectInfo, + &willInfo, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Connect packet serialized successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTConnectInfo_t connectInfo = { 0 }; +MQTTPublishInfo_t willInfo = { 0 }; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; + +// Configure connect info +connectInfo.keepAliveSeconds = 60; +connectInfo.cleanSession = true; +connectInfo.pClientIdentifier = "clientId"; +connectInfo.clientIdentifierLength = strlen("clientId"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Option 1: Without MQTT v5 properties +// Get remaining length first +status = MQTT_GetConnectPacketSize(&connectInfo, + &willInfo, + NULL, // No connect properties + NULL, // No will properties + &remainingLength, + &packetSize); + +// Serialize connect packet +status = MQTT_SerializeConnect(&connectInfo, + &willInfo, + NULL, // No connect properties + NULL, // No will properties + remainingLength, + &fixedBuffer); +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t connectProperties ; +MQTTPropBuilder_t willProperties ; +uint8_t connectPropBuffer[100]; +uint8_t willPropBuffer[100]; + +// Initialize property builders +MQTT_PropertyBuilder_Init(&connectProperties, + connectPropBuffer, + sizeof(connectPropBuffer)); +MQTT_PropertyBuilder_Init(&willProperties, + willPropBuffer, + sizeof(willPropBuffer)); + +// Add connect properties +MQTTPropAdd_SessionExpiry(&connectProperties, 3600); +MQTTPropAdd_MaxPacketSize(&connectProperties, 1024); + +// Add will properties if using will message +MQTTPropAdd_WillDelayInterval(&willProperties, 60); + +// Get remaining length first +status = MQTT_GetConnectPacketSize(&connectInfo, + &willInfo, + &connectProperties, + &willProperties, + &remainingLength, + &packetSize); + +// Serialize connect packet +status = MQTT_SerializeConnect(&connectInfo, + &willInfo, + &connectProperties, + &willProperties, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Connect packet serialized successfully +} +``` +* The `MQTT_SerializePublish` function now includes support for MQTT v5 properties with an additional parameter. Thus, the signature of `MQTT_SerializePublish changed from MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )` to `MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, const MQTTPropBuilder_t * pPublishProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTPublishInfo_t publishInfo = { 0 }; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +uint16_t packetId = 1; + +// Configure publish info +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Get remaining length first +status = MQTT_GetPublishPacketSize(&publishInfo, + &remainingLength, + &packetSize); + +// Serialize publish packet +status = MQTT_SerializePublish(&publishInfo, + packetId, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Publish packet serialized successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTPublishInfo_t publishInfo = { 0 }; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +uint16_t packetId = 1; +uint32_t maxPacketSize = pContext->connectProperties.serverMaxPacketSize ; + +// Configure publish info +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Option 1: Without MQTT v5 properties +// Get remaining length first +status = MQTT_GetPublishPacketSize(&publishInfo, + NULL, // No publish properties + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize publish packet +status = MQTT_SerializePublish(&publishInfo, + NULL, // No publish properties + packetId, + remainingLength, + &fixedBuffer); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t publishProperties = { 0 }; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&publishProperties, + propBuffer, + sizeof(propBuffer)); + +// Add publish properties +MQTTPropAdd_PubPayloadFormat(&publishProperties, 1); +MQTTPropAdd_PubTopicAlias(&publishProperties, 1); +MQTTPropAdd_PubMessageExpiry(&publishProperties, 3600); + +// Get remaining length first +status = MQTT_GetPublishPacketSize(&publishInfo, + &publishProperties, + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize publish packet +status = MQTT_SerializePublish(&publishInfo, + &publishProperties, + packetId, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Publish packet serialized successfully +} +``` +* The `MQTT_SerializePublishHeader` function now includes support for MQTT v5 properties with an additional parameter. Thus, the signature of `MQTT_SerializePublishHeader` changed from `MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer, size_t * pHeaderSize )` to `MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo, const MQTTPropBuilder_t * pPublishProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer, size_t * pHeaderSize )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTPublishInfo_t publishInfo = { 0 }; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +size_t headerSize = 0; +uint16_t packetId = 1; + +// Configure publish info +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Get remaining length first +status = MQTT_GetPublishPacketSize(&publishInfo, + &remainingLength, + &packetSize); + +// Serialize publish header +status = MQTT_SerializePublishHeader(&publishInfo, + packetId, + remainingLength, + &fixedBuffer, + &headerSize); + +if(status == MQTTSuccess) +{ + // Send header and payload separately + send(socket, fixedBuffer.pBuffer, headerSize, 0); + send(socket, publishInfo.pPayload, publishInfo.payloadLength, 0); +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTPublishInfo_t publishInfo = { 0 }; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +size_t headerSize = 0; +uint16_t packetId = 1; +uint32_t maxPacketSize = pContext->connectProperties.serverMaxPacketSize; + +// Configure publish info +publishInfo.qos = MQTTQoS1; +publishInfo.pTopicName = "topic/example"; +publishInfo.topicNameLength = strlen("topic/example"); +publishInfo.pPayload = "Hello World!"; +publishInfo.payloadLength = strlen("Hello World!"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Option 1: Without MQTT v5 properties +// Get remaining length first +status = MQTT_GetPublishPacketSize(&publishInfo, + NULL, // No publish properties + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize publish header +status = MQTT_SerializePublishHeader(&publishInfo, + NULL, // No publish properties + packetId, + remainingLength, + &fixedBuffer, + &headerSize); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t publishProperties ; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&publishProperties, + propBuffer, + sizeof(propBuffer)); + +// Add publish properties +MQTTPropAdd_PubPayloadFormat(&publishProperties, 1); +MQTTPropAdd_PubTopicAlias(&publishProperties, 1); +MQTTPropAdd_PubMessageExpiry(&publishProperties, 3600); + +// Get remaining length first +status = MQTT_GetPublishPacketSize(&publishInfo, + &publishProperties, + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize publish header +status = MQTT_SerializePublishHeader(&publishInfo, + &publishProperties, + packetId, + remainingLength, + &fixedBuffer, + &headerSize); + +if(status == MQTTSuccess) +{ + // Send header and payload separately + send(socket, fixedBuffer.pBuffer, headerSize, 0); + send(socket, publishInfo.pPayload, publishInfo.payloadLength, 0); +} +``` + +* The `MQTT_SerializeSubscribe` function now includes support for MQTT v5 properties with an additional parameter. Thus, the signature of `MQTT_SerializeSubscribe` changed from `MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )` to `MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, const MQTTPropBuilder_t * pSubscribeProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[1]; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +uint16_t packetId = 1; + +// Configure subscription list +subscriptionList[0].qos = MQTTQoS1; +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Get remaining length first +status = MQTT_GetSubscribePacketSize(subscriptionList, + 1, + &remainingLength, + &packetSize); + +// Serialize subscribe packet +status = MQTT_SerializeSubscribe(subscriptionList, + 1, + packetId, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Subscribe packet serialized successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[11]; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +uint16_t packetId = 1; +uint32_t maxPacketSize = pContext->connectProperties.serverMaxPacketSize ; + +// Configure subscription list +subscriptionList[0].qos = MQTTQoS1; +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); +subscriptionList[0].noLocalOption = false; +subscriptionList[0].retainAsPublishedOption = false; +subscriptionList[0].retainHandlingOption = retainSendOnSub; + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Option 1: Without MQTT v5 properties +// Get remaining length first +status = MQTT_GetSubscribePacketSize(subscriptionList, + 1, + NULL, // No subscribe properties + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize subscribe packet +status = MQTT_SerializeSubscribe(subscriptionList, + 1, + NULL, // No subscribe properties + packetId, + remainingLength, + &fixedBuffer); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t subscribeProperties = { 0 }; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&subscribeProperties, + propBuffer, + sizeof(propBuffer)); + +// Add subscription identifier +MQTTPropAdd_SubscribeId(&subscribeProperties, 1); + +// Get remaining length first +status = MQTT_GetSubscribePacketSize(subscriptionList, + 1, + &subscribeProperties, + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize subscribe packet +status = MQTT_SerializeSubscribe(subscriptionList, + 1, + &subscribeProperties, + packetId, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Subscribe packet serialized successfully +} +``` +* The `MQTT_SerializeUnsubscribe` function now includes support for MQTT v5 properties with an additional parameter. Thus, the signature of `MQTT_SerializeUnsubscribe` changed from `MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )` to `MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, const MQTTPropBuilder_t * pUnsubscribeProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[1]; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +uint16_t packetId = 1; + +// Configure unsubscribe list +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Get remaining length first +status = MQTT_GetUnsubscribePacketSize(subscriptionList, + 1, + &remainingLength, + &packetSize); + +// Serialize unsubscribe packet +status = MQTT_SerializeUnsubscribe(subscriptionList, + 1, + packetId, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Unsubscribe packet serialized successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTSubscribeInfo_t subscriptionList[1]; +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +uint16_t packetId = 1; +uint32_t maxPacketSize = pContext->connectProperties.serverMaxPacketSize ; + +// Configure unsubscribe list +subscriptionList[0].pTopicFilter = "topic/1"; +subscriptionList[0].topicFilterLength = strlen("topic/1"); + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Option 1: Without MQTT v5 properties +// Get remaining length first +status = MQTT_GetUnsubscribePacketSize(subscriptionList, + 1, + NULL, // No unsubscribe properties + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize unsubscribe packet +status = MQTT_SerializeUnsubscribe(subscriptionList, + 1, + NULL, // No unsubscribe properties + packetId, + remainingLength, + &fixedBuffer); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t unsubscribeProperties ; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&unsubscribeProperties, + propBuffer, + sizeof(propBuffer)); + +// Add user property +MQTTUserProperty_t userProperty = { + .pKey = "key", + .keyLength = strlen("key"), + .pValue = "value", + .valueLength = strlen("value") +}; +MQTTPropAdd_UserProp(&unsubscribeProperties, &userProperty); + +// Get remaining length first +status = MQTT_GetUnsubscribePacketSize(subscriptionList, + 1, + &unsubscribeProperties, + &remainingLength, + &packetSize, + maxPacketSize); + +// Serialize unsubscribe packet +status = MQTT_SerializeUnsubscribe(subscriptionList, + 1, + &unsubscribeProperties, + packetId, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Unsubscribe packet serialized successfully +} +``` +* The `MQTT_SerializeDisconnect` function now includes support for MQTT v5 properties and reason codes with three additional parameters. Thus, the signature of `MQTT_SerializeDisconnect` changed from `MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer )` to `MQTTStatus_t MQTT_SerializeDisconnect( const MQTTPropBuilder_t *pDisconnectProperties, MQTTSuccessFailReasonCode_t reasonCode, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Serialize disconnect packet +status = MQTT_SerializeDisconnect(&fixedBuffer); + +if(status == MQTTSuccess) +{ + // Disconnect packet serialized successfully +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTFixedBuffer_t fixedBuffer; +uint8_t buffer[BUFFER_SIZE]; +size_t remainingLength = 0; +uint32_t maxPacketSize = pContext->connectProperties.serverMaxPacketSize ; + +// Configure fixed buffer +fixedBuffer.pBuffer = buffer; +fixedBuffer.size = BUFFER_SIZE; + +// Option 1: Without MQTT v5 properties +// Get remaining length first +status = MQTT_GetDisconnectPacketSize(NULL, // No disconnect properties + &remainingLength, + &packetSize, + maxPacketSize, + 0); // Default reason code + +// Serialize disconnect packet +status = MQTT_SerializeDisconnect(NULL, // No disconnect properties + 0, // Default reason code + remainingLength, + &fixedBuffer); + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t disconnectProperties ; +uint8_t propBuffer[100]; + +// Initialize property builder +MQTT_PropertyBuilder_Init(&disconnectProperties, + propBuffer, + sizeof(propBuffer)); + +// Add disconnect properties +MQTTPropAdd_SessionExpiry(&disconnectProperties, 0); +MQTTPropAdd_ReasonString(&disconnectProperties, + "Normal shutdown", + strlen("Normal shutdown")); + +// Get remaining length first +status = MQTT_GetDisconnectPacketSize(&disconnectProperties, + &remainingLength, + &packetSize, + maxPacketSize, + MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION); + +// Serialize disconnect packet +status = MQTT_SerializeDisconnect(&disconnectProperties, + MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, + remainingLength, + &fixedBuffer); + +if(status == MQTTSuccess) +{ + // Disconnect packet serialized successfully +} +``` +* The `MQTT_DeserializePublish` function now includes support for MQTT v5 properties with three additional parameters. Thus, the signature of `MQTT_DeserializePublish` changed from `MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket, uint16_t * pPacketId, MQTTPublishInfo_t * pPublishInfo )` to `MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket, uint16_t * pPacketId, MQTTPublishInfo_t * pPublishInfo, MQTTPropBuilder_t * propBuffer, uint32_t maxPacketSize, uint16_t topicAliasMax )`. For example: + +**Old Code Snippet** +``` +// Variables used in this example. +MQTTPacketInfo_t incomingPacket; +MQTTPublishInfo_t publishInfo = { 0 }; +uint16_t packetId; + +// Assume incomingPacket is populated from network + +if((incomingPacket.type & 0xF0) == MQTT_PACKET_TYPE_PUBLISH) +{ + status = MQTT_DeserializePublish(&incomingPacket, + &packetId, + &publishInfo); + + if(status == MQTTSuccess) + { + // Handle received publish + handlePublish(&publishInfo); + } +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTPacketInfo_t incomingPacket; +MQTTPublishInfo_t publishInfo = { 0 }; +uint16_t packetId; + +uint16_t topicAliasMax = pContext->connectProperties.topicAliasMax ; +uint32_t maxPacketSize = pContext->connectProperties.maxPacketSize ; + +// Option 1: Without MQTT v5 properties +if((incomingPacket.type & 0xF0) == MQTT_PACKET_TYPE_PUBLISH) +{ + status = MQTT_DeserializePublish(&incomingPacket, + &packetId, + &publishInfo, + NULL, // No property buffer + maxPacketSize, // No max packet size + topicAliasMax); // No topic alias maximum + + if(status == MQTTSuccess) + { + // Handle received publish + handlePublish(&publishInfo); + } +} + +// Option 2: With MQTT v5 properties +MQTTPropBuilder_t propBuffer = { 0 }; +uint8_t buffer[100]; + +// Initialize property buffer +MQTT_PropertyBuilder_Init(&propBuffer, + buffer, + sizeof(buffer)); + +if((incomingPacket.type & 0xF0) == MQTT_PACKET_TYPE_PUBLISH) +{ + status = MQTT_DeserializePublish(&incomingPacket, + &packetId, + &publishInfo, + &propBuffer, + maxPacketSize, + topicAliasMax); + + if(status == MQTTSuccess) + { + // Access publish properties if needed + uint8_t payloadFormat; + if(MQTTPropGet_PubPayloadFormat(&propBuffer, &payloadFormat) == MQTTSuccess) + { + // Handle payload format + } + + uint16_t topicAlias; + if(MQTTPropGet_PubTopicAlias(&propBuffer, &topicAlias) == MQTTSuccess) + { + // Handle topic alias + } + + // Handle received publish + handlePublish(&publishInfo); + } +} +``` +* The `MQTT_DeserializeAck` function now includes support for MQTT v5 properties and reason codes with five additional parameters. Thus, the signature of `MQTT_DeserializeAck` changed from `MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket, uint16_t * pPacketId, bool * pSessionPresent )` to `MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket, uint16_t * pPacketId, bool * pSessionPresent, MQTTReasonCodeInfo_t * pReasonCode, bool requestProblem, uint32_t maxPacketSize, MQTTPropBuilder_t * propBuffer, MQTTConnectProperties_t * pConnectProperties )`. For example: + +**Old Code Snippet** +``` +/ Variables used in this example. +MQTTPacketInfo_t incomingPacket; +uint16_t packetId; +bool sessionPresent; + +// Assume incomingPacket is populated from network + +status = MQTT_DeserializeAck(&incomingPacket, + &packetId, + &sessionPresent); + +if(status == MQTTSuccess) +{ + // Handle acknowledgment +} +``` +**New Code Snippet** +``` +// Variables used in this example. +MQTTPacketInfo_t incomingPacket; +uint16_t packetId; +bool sessionPresent; +MQTTPropBuilder_t propBuffer ; // Can be set to NULL if the user does not want any incoming properties. + +MQTTReasonCodeInfo_t reasonCode ; // Can be set to NULL if the incoming packet is CONNACK or PINGRESP + +MQTTConnectProperties_t connectProperties = pContext->connectProperties; // Can be set to NULL if the incoming packet is PUBLISH ACKs, SUBACK, UNSUBACK or PINGRESP + +bool requestProblem = pContext->connectProperties.requestProblemInfo ; // only relevant if the incoming packet is a PUBLISH Ack. + +uint32_t maxPacketSize = pContext->connectProperties.maxPacketSize ; + +status = MQTT_DeserializeAck(&incomingPacket, + &packetId, + &sessionPresent, + &reasonCode, + requestProblem, + maxPacketSize, + &propBuffer, + &connectProperties); + +if(status == MQTTSuccess) +{ + // Handle acknowledgment based on packet type + +} +``` + + + + + + + + + + + diff --git a/README.md b/README.md index 4309f2622..f452603d0 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ **[API Documentation Pages for current and previous releases of this library can be found here](https://freertos.github.io/coreMQTT/)** This repository contains the coreMQTT library that has been optimized for a low -memory footprint. The coreMQTT library is compliant with the -[MQTT 3.1.1](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html) +memory footprint. standard. It has no dependencies on any additional libraries other than the standard C library, a customer-implemented network transport interface, and _optionally_ a user-implemented platform time function. This library is @@ -21,6 +20,10 @@ library has also undergone both static code analysis from safety through the [CBMC automated reasoning tool](https://www.cprover.org/cbmc/). +-For MQTT v3.1.1 [(MQTTv3 Specification)](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html), use code from tag : [coreMQTT v2.3.1](https://github.com/FreeRTOS/coreMQTT/tree/v2.3.1) + +-For MQTT v5.0 [(MQTTv5 Specification)](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html), use code from tag : [coreMQTT v3.0.0](https://github.com/FreeRTOS/coreMQTT/tree/v3.0.0) + See memory requirements for this library [here](./docs/doxygen/include/size_table.md). @@ -109,10 +112,15 @@ connectInfo.userNameLength = USERNAME_STRING_LENGTH; mqttStatus = MQTT_Connect( pMqttContext, &connectInfo, NULL, CONNACK_RECV_TIMEOUT_MS, pSessionPresent ); ``` -## Upgrading to v2.0.0 and above +## Upgrading to v2.x + +With coreMQTT versions v2.x, there are breaking changes. Please refer to the +[coreMQTT version v2.x Migration Guide](MigrationGuide.md). + +## Upgrading to v3.0.0 and above -With coreMQTT versions >=v2.0.0, there are breaking changes. Please refer to the -[coreMQTT version >=v2.0.0 Migration Guide](MigrationGuide.md). +With coreMQTT versions >=v3.0.0, there are breaking changes. Please refer to the +[coreMQTT version >=v3.0.0 Migration Guide](MigrationGuide.md) ## Building the Library diff --git a/docs/doxygen/config.doxyfile b/docs/doxygen/config.doxyfile index ca0047ec4..1f0be8717 100644 --- a/docs/doxygen/config.doxyfile +++ b/docs/doxygen/config.doxyfile @@ -54,7 +54,7 @@ PROJECT_NUMBER = v2.3.1+ # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "MQTT 3.1.1 Client Library" +PROJECT_BRIEF = "MQTT 5.0 Client Library" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 diff --git a/docs/doxygen/include/size_table.md b/docs/doxygen/include/size_table.md index 5f654632f..2c7eb0995 100644 --- a/docs/doxygen/include/size_table.md +++ b/docs/doxygen/include/size_table.md @@ -9,8 +9,8 @@ core_mqtt.c -
4.9K
-
4.2K
+
7.0K
+
6.0K
core_mqtt_state.c @@ -19,12 +19,12 @@ core_mqtt_serializer.c -
2.9K
-
2.3K
+
11.5K
+
8.1K
Total estimates -
9.5K
-
7.8K
+
20.2K
+
15.4K
diff --git a/docs/doxygen/pages.dox b/docs/doxygen/pages.dox index 750df5336..8338e16fc 100644 --- a/docs/doxygen/pages.dox +++ b/docs/doxygen/pages.dox @@ -1,12 +1,12 @@ /** @mainpage Overview @anchor mqtt -@brief MQTT 3.1.1 client library +@brief MQTT 5.0 client library > MQTT stands for MQ Telemetry Transport. It is a publish/subscribe, extremely simple and lightweight messaging protocol, designed for constrained devices and low-bandwidth, high-latency or unreliable networks. The design principles are to minimise network bandwidth and device resource requirements whilst also attempting to ensure reliability and some degree of assurance of delivery. These principles also turn out to make the protocol ideal of the emerging "machine-to-machine" (M2M) or "Internet of Things" world of connected devices, and for mobile applications where bandwidth and battery power are at a premium. Official description of MQTT from [mqtt.org](https://mqtt.org)
-This MQTT library implements the client side of the MQTT 3.1.1 protocol. This library is optimized for resource constrained embedded devices. Features of this library include: +This MQTT library implements the client side of the MQTT 5.0 protocol. This library is optimized for resource constrained embedded devices. Features of this library include: - Fully synchronous API, to allow applications to completely manage their concurrency and multi-threading method. - Operations on fixed buffers, so that applications may control their memory allocation strategy. - Scalable performance and footprint. The [configuration settings](@ref core_mqtt_config) allow this library to be tailored to a system's resources. @@ -23,7 +23,7 @@ Please see https://github.com/aws/aws-iot-device-sdk-embedded-C/tree/main/demos/ @page mqtt_design Design @brief Architecture of the MQTT library. -This MQTT client library provides an implementation of the [MQTT 3.1.1 specification](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html). It is optimized for resource constrained devices and does not allocate any memory. +This MQTT client library provides an implementation of the [MQTT 5.0 specification](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html). It is optimized for resource constrained devices and does not allocate any memory. @section mqtt_interfaces Interfaces and Callbacks @@ -66,23 +66,28 @@ these low-level functions can be used directly: - @ref mqtt_serializeconnect_function
- @ref mqtt_getsubscribepacketsize_function
- @ref mqtt_serializesubscribe_function
+- @ref mqtt_validatesubscribeproperties_function
- @ref mqtt_getunsubscribepacketsize_function
- @ref mqtt_serializeunsubscribe_function
- @ref mqtt_getpublishpacketsize_function
- @ref mqtt_serializepublish_function
- @ref mqtt_serializepublishheader_function
+- @ref mqtt_serializepublishheaderwithouttopic_function
+- @ref mqtt_validatepublishparams_function
+- @ref mqtt_validatepublishproperties_function
- @ref mqtt_serializeack_function
+- @ref mqtt_getackpacketsize_function
- @ref mqtt_getdisconnectpacketsize_function
-- @ref mqtt_serializedisconnect_function
- @ref mqtt_getpingreqpacketsize_function
- @ref mqtt_serializepingreq_function
- @ref mqtt_deserializepublish_function
- @ref mqtt_deserializeack_function
+- @ref mqtt_deserializedisconnect_function
- @ref mqtt_getincomingpackettypeandlength_function
@section mqtt_sessions Sessions and State -The MQTT 3.1.1 protocol allows for a client and server to maintain persistent sessions, which +The MQTT 5.0 protocol allows for a client and server to maintain persistent sessions, which can be resumed after a reconnect. The elements of a session stored by this client library consist of the states of incomplete publishes with Quality of Service levels of 1 (at least once), or 2 (exactly once). These states are stored in the pointers pointed to by @ref MQTTContext_t.outgoingPublishRecords and @ref MQTTContext_t.incomingPublishRecords; @@ -107,14 +112,14 @@ The exception is @ref mqtt_connect_function; since a MQTT session cannot be cons the function waits until the CONNACK is received. @subsection mqtt_receivetimeout Runtime Timeouts passed to MQTT library -@ref mqtt_connect_function, @ref mqtt_processloop_function, and @ref mqtt_receiveloop_function all accept a timeout parameter for packet reception.
+@ref mqtt_connect_function accepts a timeout parameter for packet reception.
For the @ref mqtt_connect_function, if this value is set to 0, then instead of a time-based loop, it will attempt to call the transport receive function up to a maximum number of retries, which is defined by @ref MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT. -For @ref mqtt_processloop_function and @ref mqtt_receiveloop_function, the timeout value represents the minimum duration that will be spent in the function, provided there are no network errors. -Should the timeout be set to 0, then the loop will run for a single iteration. A single iteration of a loop consists of an attempt to receive a single byte from the network, and -if the single byte receive was successful, then attempt(s) to receive the rest of the packet (with retry attempts governed by @ref MQTT_RECV_POLLING_TIMEOUT_MS), followed by sending acknowledgement response, if needed -(with retry attempts governed by @ref MQTT_SEND_TIMEOUT_MS), and then, finally deserialization of the packet received and a call to the application callback. +For @ref mqtt_processloop_function and @ref mqtt_receiveloop_function, the timeout functionality is handled by the user application. It is up to the user to define the number of times these functions will be called, provided that there are no network errors. +A single call to the function consists of an attempt to receive all available bytes from the network, and if the read was successful, then attempt(s) to receive the rest of the packet (with retry attempts governed by @ref MQTT_RECV_POLLING_TIMEOUT_MS), followed by the deserialization of the received packet. +After this a call to application callback is invoked, followed by sending acknowledgement response, if needed +(with retry attempts governed by @ref MQTT_SEND_TIMEOUT_MS). If the first read did not succeed, then instead the library checks if a ping request needs to be sent (only for the process loop). See the below diagrams for a representation of the above flows: @@ -177,6 +182,7 @@ Some configuration settings are C pre-processor constants, and some are function @page mqtt_functions Functions @brief Primary functions of the MQTT library:

@subpage mqtt_init_function
+@subpage mqtt_propertybuilder_init_function
@subpage mqtt_connect_function
@subpage mqtt_subscribe_function
@subpage mqtt_publish_function
@@ -195,20 +201,73 @@ Serializer functions of the MQTT library:

@subpage mqtt_serializeconnect_function
@subpage mqtt_getsubscribepacketsize_function
@subpage mqtt_serializesubscribe_function
+@subpage mqtt_validatesubscribeproperties_function
@subpage mqtt_getunsubscribepacketsize_function
@subpage mqtt_serializeunsubscribe_function
@subpage mqtt_getpublishpacketsize_function
@subpage mqtt_serializepublish_function
@subpage mqtt_serializepublishheader_function
+@subpage mqtt_serializepublishheaderwithouttopic_function
+@subpage mqtt_validatepublishparams_function
+@subpage mqtt_validatepublishproperties_function
@subpage mqtt_serializeack_function
+@subpage mqtt_getackpacketsize_function
@subpage mqtt_getdisconnectpacketsize_function
-@subpage mqtt_serializedisconnect_function
@subpage mqtt_getpingreqpacketsize_function
@subpage mqtt_serializepingreq_function
@subpage mqtt_deserializepublish_function
@subpage mqtt_deserializeack_function
+@subpage mqtt_deserializedisconnect_function
@subpage mqtt_getincomingpackettypeandlength_function
+ +Property Add functions:

+@subpage mqttpropadd_subscribeid_function
+@subpage mqttpropadd_userprop_function
+@subpage mqttpropadd_sessionexpiry_function
+@subpage mqttpropadd_connreceivemax_function
+@subpage mqttpropadd_connmaxpacketsize_function
+@subpage mqttpropadd_conntopicaliasmax_function
+@subpage mqttpropadd_connrequestrespinfo_function
+@subpage mqttpropadd_connrequestprobinfo_function
+@subpage mqttpropadd_connauthmethod_function
+@subpage mqttpropadd_connauthdata_function
+@subpage mqttpropadd_willdelayinterval_function
+@subpage mqttpropadd_pubpayloadformat_function
+@subpage mqttpropadd_pubmessageexpiry_function
+@subpage mqttpropadd_pubtopicalias_function
+@subpage mqttpropadd_pubresponsetopic_function
+@subpage mqttpropadd_pubcorrelationdata_function
+@subpage mqttpropadd_pubcontenttype_function
+@subpage mqttpropadd_reasonstring_function
+ +Property Get functions:

+@subpage mqtt_incominggetnextprop_function
+@subpage mqttpropget_pubtopicalias_function
+@subpage mqttpropget_pubpayloadformat_function
+@subpage mqttpropget_pubresponsetopic_function
+@subpage mqttpropget_pubcorrelationdata_function
+@subpage mqttpropget_pubmessageexpiryinterval_function
+@subpage mqttpropget_pubcontenttype_function
+@subpage mqttpropget_pubsubscriptionid_function
+@subpage mqttpropget_userprop_function
+@subpage mqttpropget_reasonstring_function
+@subpage mqttpropget_disconnectserverref_function
+@subpage mqttpropget_sessionexpiry_function
+@subpage mqttpropget_conntopicaliasmax_function
+@subpage mqttpropget_connreceivemax_function
+@subpage mqttpropget_connmaxqos_function
+@subpage mqttpropget_connretainavailable_function
+@subpage mqttpropget_connmaxpacketsize_function
+@subpage mqttpropget_connclientid_function
+@subpage mqttpropget_connwildcard_function
+@subpage mqttpropget_connsubid_function
+@subpage mqttpropget_connsharedsubavailable_function
+@subpage mqttpropget_connserverkeepalive_function
+@subpage mqttpropget_connresponseinfo_function
+@subpage mqttpropget_connauthmethod_function
+@subpage mqttpropget_connauthdata_function
+ @page mqtt_init_function MQTT_Init @snippet core_mqtt.h declare_mqtt_init @copydoc MQTT_Init @@ -261,6 +320,10 @@ Serializer functions of the MQTT library:

@snippet core_mqtt_state.h declare_mqtt_publishtoresend @copydoc MQTT_PublishToResend +@page mqtt_propertybuilder_init_function MQTT_PropertyBuilder_Init +@snippet core_mqtt_serializer.h declare_mqtt_propertybuilder_init +@copydoc MQTT_PropertyBuilder_Init + @page mqtt_getconnectpacketsize_function MQTT_GetConnectPacketSize @snippet core_mqtt_serializer.h declare_mqtt_getconnectpacketsize @copydoc MQTT_GetConnectPacketSize @@ -285,6 +348,10 @@ Serializer functions of the MQTT library:

@snippet core_mqtt_serializer.h declare_mqtt_serializeunsubscribe @copydoc MQTT_SerializeUnsubscribe +@page mqtt_validatepublishparams_function MQTT_ValidatePublishParams +@snippet core_mqtt_serializer.h declare_mqtt_validatepublishparams +@copydoc MQTT_ValidatePublishParams + @page mqtt_getpublishpacketsize_function MQTT_GetPublishPacketSize @snippet core_mqtt_serializer.h declare_mqtt_getpublishpacketsize @copydoc MQTT_GetPublishPacketSize @@ -293,22 +360,30 @@ Serializer functions of the MQTT library:

@snippet core_mqtt_serializer.h declare_mqtt_serializepublish @copydoc MQTT_SerializePublish +@page mqtt_validatepublishproperties_function MQTT_ValidatePublishProperties +@snippet core_mqtt_serializer.h declare_mqtt_validatepublishproperties +@copydoc MQTT_ValidatePublishProperties + @page mqtt_serializepublishheader_function MQTT_SerializePublishHeader @snippet core_mqtt_serializer.h declare_mqtt_serializepublishheader @copydoc MQTT_SerializePublishHeader +@page mqtt_serializepublishheaderwithouttopic_function MQTT_SerializePublishHeaderWithoutTopic +@snippet core_mqtt_serializer.h declare_mqtt_serializepublishheaderwithouttopic +@copydoc MQTT_SerializePublishHeaderWithoutTopic + @page mqtt_serializeack_function MQTT_SerializeAck @snippet core_mqtt_serializer.h declare_mqtt_serializeack @copydoc MQTT_SerializeAck +@page mqtt_getackpacketsize_function MQTT_GetAckPacketSize +@snippet core_mqtt_serializer.h declare_mqtt_serializeack +@copydoc MQTT_GetAckPacketSize + @page mqtt_getdisconnectpacketsize_function MQTT_GetDisconnectPacketSize @snippet core_mqtt_serializer.h declare_mqtt_getdisconnectpacketsize @copydoc MQTT_GetDisconnectPacketSize -@page mqtt_serializedisconnect_function MQTT_SerializeDisconnect -@snippet core_mqtt_serializer.h declare_mqtt_serializedisconnect -@copydoc MQTT_SerializeDisconnect - @page mqtt_getpingreqpacketsize_function MQTT_GetPingreqPacketSize @snippet core_mqtt_serializer.h declare_mqtt_getpingreqpacketsize @copydoc MQTT_GetPingreqPacketSize @@ -325,9 +400,190 @@ Serializer functions of the MQTT library:

@snippet core_mqtt_serializer.h declare_mqtt_deserializeack @copydoc MQTT_DeserializeAck +@page mqtt_deserializedisconnect_function MQTT_DeserializeDisconnect +@snippet core_mqtt_serializer.h declare_mqtt_deserializedisconnect +@copydoc MQTT_DeserializeDisconnect + @page mqtt_getincomingpackettypeandlength_function MQTT_GetIncomingPacketTypeAndLength @snippet core_mqtt_serializer.h declare_mqtt_getincomingpackettypeandlength @copydoc MQTT_GetIncomingPacketTypeAndLength + +@page mqttpropadd_subscribeid_function MQTTPropAdd_SubscribeId +@snippet core_mqtt_serializer.h declare_mqttpropadd_subscribeid +@copydoc MQTTPropAdd_SubscribeId + +@page mqttpropadd_userprop_function MQTTPropAdd_UserProp +@snippet core_mqtt_serializer.h declare_mqttpropadd_userprop +@copydoc MQTTPropAdd_UserProp + +@page mqttpropadd_sessionexpiry_function MQTTPropAdd_SessionExpiry +@snippet core_mqtt_serializer.h declare_mqttpropadd_sessionexpiry +@copydoc MQTTPropAdd_SessionExpiry + +@page mqttpropadd_willdelayinterval_function MQTTPropAdd_WillDelayInterval +@snippet core_mqtt_serializer.h declare_mqttpropadd_willdelayinterval +@copydoc MQTTPropAdd_WillDelayInterval + +@page mqttpropadd_connreceivemax_function MQTTPropAdd_ConnReceiveMax +@snippet core_mqtt_serializer.h declare_mqttpropadd_connreceivemax +@copydoc MQTTPropAdd_ConnReceiveMax + +@page mqttpropadd_connmaxpacketsize_function MQTTPropAdd_ConnMaxPacketSize +@snippet core_mqtt_serializer.h declare_mqttpropadd_connmaxpacketsize +@copydoc MQTTPropAdd_ConnMaxPacketSize + +@page mqttpropadd_conntopicaliasmax_function MQTTPropAdd_ConnTopicAliasMax +@snippet core_mqtt_serializer.h declare_mqttpropadd_conntopicaliasmax +@copydoc MQTTPropAdd_ConnTopicAliasMax + +@page mqttpropadd_connrequestrespinfo_function MQTTPropAdd_ConnRequestRespInfo +@snippet core_mqtt_serializer.h declare_mqttpropadd_connrequestrespinfo +@copydoc MQTTPropAdd_ConnRequestRespInfo + +@page mqttpropadd_connrequestprobinfo_function MQTTPropAdd_ConnRequestProbInfo +@snippet core_mqtt_serializer.h declare_mqttpropadd_connrequestprobinfo +@copydoc MQTTPropAdd_ConnRequestProbInfo + +@page mqttpropadd_connauthmethod_function MQTTPropAdd_ConnAuthMethod +@snippet core_mqtt_serializer.h declare_mqttpropadd_connauthmethod +@copydoc MQTTPropAdd_ConnAuthMethod + +@page mqttpropadd_connauthdata_function MQTTPropAdd_ConnAuthData +@snippet core_mqtt_serializer.h declare_mqttpropadd_connauthdata +@copydoc MQTTPropAdd_ConnAuthData + +@page mqttpropadd_pubpayloadformat_function MQTTPropAdd_PubPayloadFormat +@snippet core_mqtt_serializer.h declare_mqttpropadd_pubpayloadformat +@copydoc MQTTPropAdd_PubPayloadFormat + +@page mqttpropadd_pubmessageexpiry_function MQTTPropAdd_PubMessageExpiry +@snippet core_mqtt_serializer.h declare_mqttpropadd_pubmessageexpiry +@copydoc MQTTPropAdd_PubMessageExpiry + +@page mqttpropadd_pubtopicalias_function MQTTPropAdd_PubTopicAlias +@snippet core_mqtt_serializer.h declare_mqttpropadd_pubtopicalias +@copydoc MQTTPropAdd_PubTopicAlias + +@page mqttpropadd_pubresponsetopic_function MQTTPropAdd_PubResponseTopic +@snippet core_mqtt_serializer.h declare_mqttpropadd_pubresponsetopic +@copydoc MQTTPropAdd_PubResponseTopic + +@page mqttpropadd_pubcorrelationdata_function MQTTPropAdd_PubCorrelationData +@snippet core_mqtt_serializer.h declare_mqttpropadd_pubcorrelationdata +@copydoc MQTTPropAdd_PubCorrelationData + +@page mqttpropadd_pubcontenttype_function MQTTPropAdd_PubContentType +@snippet core_mqtt_serializer.h declare_mqttpropadd_pubcontenttype +@copydoc MQTTPropAdd_PubContentType + +@page mqttpropadd_reasonstring_function MQTTPropAdd_ReasonString +@snippet core_mqtt_serializer.h declare_mqttpropadd_reasonstring +@copydoc MQTTPropAdd_ReasonString + +@page mqtt_validatesubscribeproperties_function MQTT_ValidateSubscribeProperties +@snippet core_mqtt_serializer.h declare_mqtt_validatesubscribeproperties +@copydoc MQTT_ValidateSubscribeProperties + +@page mqttpropget_pubtopicalias_function MQTTPropGet_PubTopicAlias +@snippet core_mqtt_serializer.h declare_mqttpropget_pubtopicalias +@copydoc MQTTPropGet_PubTopicAlias + +@page mqttpropget_pubpayloadformat_function MQTTPropGet_PubPayloadFormatIndicator +@snippet core_mqtt_serializer.h declare_mqttpropget_pubpayloadformat +@copydoc MQTTPropGet_PubPayloadFormatIndicator + +@page mqttpropget_pubresponsetopic_function MQTTPropGet_PubResponseTopic +@snippet core_mqtt_serializer.h declare_mqttpropget_pubresponsetopic +@copydoc MQTTPropGet_PubResponseTopic + +@page mqttpropget_pubcorrelationdata_function MQTTPropGet_PubCorrelationData +@snippet core_mqtt_serializer.h declare_mqttpropget_pubcorrelationdata +@copydoc MQTTPropGet_PubCorrelationData + +@page mqttpropget_pubmessageexpiryinterval_function MQTTPropGet_PubMessageExpiryInterval +@snippet core_mqtt_serializer.h declare_mqttpropget_pubmessageexpiryinterval +@copydoc MQTTPropGet_PubMessageExpiryInterval + +@page mqttpropget_pubcontenttype_function MQTTPropGet_PubContentType +@snippet core_mqtt_serializer.h declare_mqttpropget_pubcontenttype +@copydoc MQTTPropGet_PubContentType + +@page mqttpropget_pubsubscriptionid_function MQTTPropGet_PubSubscriptionId +@snippet core_mqtt_serializer.h declare_mqttpropget_pubsubscriptionid +@copydoc MQTTPropGet_PubSubscriptionId + +@page mqttpropget_userprop_function MQTTPropGet_UserProp +@snippet core_mqtt_serializer.h declare_mqttpropget_userprop +@copydoc MQTTPropGet_UserProp + +@page mqttpropget_reasonstring_function MQTTPropGet_ReasonString +@snippet core_mqtt_serializer.h declare_mqttpropget_reasonstring +@copydoc MQTTPropGet_ReasonString + +@page mqttpropget_disconnectserverref_function MQTTPropGet_ServerRef +@snippet core_mqtt_serializer.h declare_mqttpropget_disconnectserverref +@copydoc MQTTPropGet_ServerRef + +@page mqttpropget_sessionexpiry_function MQTTPropGet_SessionExpiry +@snippet core_mqtt_serializer.h declare_mqttpropget_sessionexpiry +@copydoc MQTTPropGet_SessionExpiry + +@page mqttpropget_conntopicaliasmax_function MQTTPropGet_ConnTopicAliasMax +@snippet core_mqtt_serializer.h declare_mqttpropget_conntopicaliasmax +@copydoc MQTTPropGet_ConnTopicAliasMax + +@page mqttpropget_connreceivemax_function MQTTPropGet_ConnReceiveMax +@snippet core_mqtt_serializer.h declare_mqttpropget_connreceivemax +@copydoc MQTTPropGet_ConnReceiveMax + +@page mqttpropget_connmaxqos_function MQTTPropGet_ConnMaxQos +@snippet core_mqtt_serializer.h declare_mqttpropget_connmaxqos +@copydoc MQTTPropGet_ConnMaxQos + +@page mqttpropget_connretainavailable_function MQTTPropGet_ConnRetainAvailable +@snippet core_mqtt_serializer.h declare_mqttpropget_connretainavailable +@copydoc MQTTPropGet_ConnRetainAvailable + +@page mqttpropget_connmaxpacketsize_function MQTTPropGet_ConnMaxPacketSize +@snippet core_mqtt_serializer.h declare_mqttpropget_connmaxpacketsize +@copydoc MQTTPropGet_ConnMaxPacketSize + +@page mqttpropget_connclientid_function MQTTPropGet_ConnClientId +@snippet core_mqtt_serializer.h declare_mqttpropget_connclientid +@copydoc MQTTPropGet_ConnClientId + +@page mqttpropget_connwildcard_function MQTTPropGet_ConnWildcard +@snippet core_mqtt_serializer.h declare_mqttpropget_connwildcard +@copydoc MQTTPropGet_ConnWildcard + +@page mqttpropget_connsubid_function MQTTPropGet_ConnSubId +@snippet core_mqtt_serializer.h declare_mqttpropget_connsubid +@copydoc MQTTPropGet_ConnSubId + +@page mqttpropget_connsharedsubavailable_function MQTTPropGet_ConnSharedSubAvailable +@snippet core_mqtt_serializer.h declare_mqttpropget_connsharedsubavailable +@copydoc MQTTPropGet_ConnSharedSubAvailable + +@page mqttpropget_connserverkeepalive_function MQTTPropGet_ConnServerKeepAlive +@snippet core_mqtt_serializer.h declare_mqttpropget_connserverkeepalive +@copydoc MQTTPropGet_ConnServerKeepAlive + +@page mqttpropget_connresponseinfo_function MQTTPropGet_ConnResponseInfo +@snippet core_mqtt_serializer.h declare_mqttpropget_connresponseinfo +@copydoc MQTTPropGet_ConnResponseInfo + +@page mqttpropget_connauthmethod_function MQTTPropGet_ConnAuthMethod +@snippet core_mqtt_serializer.h declare_mqttpropget_connauthmethod +@copydoc MQTTPropGet_ConnAuthMethod + +@page mqttpropget_connauthdata_function MQTTPropGet_ConnAuthData +@snippet core_mqtt_serializer.h declare_mqttpropget_connauthdata +@copydoc MQTTPropGet_ConnAuthData + +@page mqtt_incominggetnextprop_function MQTT_IncomingGetNextProp +@snippet core_mqtt_serializer.h declare_mqtt_incominggetnextprop +@copydoc MQTT_IncomingGetNextProp + */ /** diff --git a/docs/plantuml/images/mqtt_connect_design.png b/docs/plantuml/images/mqtt_connect_design.png index 81a1933c6..c59463804 100644 Binary files a/docs/plantuml/images/mqtt_connect_design.png and b/docs/plantuml/images/mqtt_connect_design.png differ diff --git a/docs/plantuml/images/mqtt_processloop_design.png b/docs/plantuml/images/mqtt_processloop_design.png index cffe3d256..9e730d292 100644 Binary files a/docs/plantuml/images/mqtt_processloop_design.png and b/docs/plantuml/images/mqtt_processloop_design.png differ diff --git a/docs/plantuml/images/mqtt_receiveloop_design.png b/docs/plantuml/images/mqtt_receiveloop_design.png index d86b72b52..fe9b73581 100644 Binary files a/docs/plantuml/images/mqtt_receiveloop_design.png and b/docs/plantuml/images/mqtt_receiveloop_design.png differ diff --git a/docs/plantuml/mqtt_connect_design.pu b/docs/plantuml/mqtt_connect_design.pu deleted file mode 100644 index e89eb4a6b..000000000 --- a/docs/plantuml/mqtt_connect_design.pu +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -skinparam dpi 300 -skinparam ArrowFontSize 18 - -start -: Send CONNECT packet; -: count = 0; - -repeat - : Receive single byte; -repeat while ( No network data available AND \n retry count < MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT) is (yes) --> no or timeout == 0; - -repeat - : Get rest of CONNACK packet; - note left: Retry zero byte reads for maximum period \nof **MQTT_RECV_POLLING_TIMEOUT_MS** -repeat while( Received complete packet? ) is ( no ) -: Deserialize CONNACK packet; -stop - -@enduml diff --git a/docs/plantuml/mqtt_processloop_design.pu b/docs/plantuml/mqtt_processloop_design.pu deleted file mode 100644 index 2549b27f4..000000000 --- a/docs/plantuml/mqtt_processloop_design.pu +++ /dev/null @@ -1,32 +0,0 @@ -@startuml -skinparam dpi 300 -skinparam ArrowFontSize 18 - -start - -repeat - : Receive single byte; - if( read successful? ) then (yes) - repeat - : Get rest of packet; - note left: Retry zero byte reads for maximum period \nof **MQTT_RECV_POLLING_TIMEOUT_MS** - repeat while( Received complete packet? ) is ( no ) - : Deserialize packet; - if ( Need to send ACK response? ) then (yes) - repeat - : Send ACK packet; - note left: Retry zero byte sends for maximum period \nof **MQTT_SEND_RETRY_TIMEOUT_MS** - repeat while( Sent complete packet? ) is ( no ) - else (no) - endif - : Invoke Application callback; - else (no) - : Manage Keep-Alive; - endif - -repeat while (**timeout** reached) is (no) --> yes or timeout == 0; - -stop - -@enduml diff --git a/docs/plantuml/mqtt_receiveloop_design.pu b/docs/plantuml/mqtt_receiveloop_design.pu deleted file mode 100644 index 845eaa874..000000000 --- a/docs/plantuml/mqtt_receiveloop_design.pu +++ /dev/null @@ -1,30 +0,0 @@ -@startuml -skinparam dpi 300 -skinparam ArrowFontSize 18 - -start - -repeat - : Receive single byte; - if( read successful? ) then (yes) - repeat - : Get rest of packet; - note left: Retry zero byte reads for maximum period \nof **MQTT_RECV_POLLING_TIMEOUT_MS** - repeat while( Received complete packet? ) is ( no ) - : Deserialize packet; - if ( Need to send ACK response? ) then (yes) - repeat - : Send ACK packet; - note left: Retry zero byte sends for maximum period \nof **MQTT_SEND_RETRY_TIMEOUT_MS** - repeat while( Sent complete packet? ) is ( no ) - else (no) - endif - else (no) - endif - -repeat while (**timeout** reached) is (no) --> yes or timeout == 0; - -stop - -@enduml diff --git a/manifest.yml b/manifest.yml index 9d693767a..3e528b700 100644 --- a/manifest.yml +++ b/manifest.yml @@ -1,5 +1,5 @@ name : "coreMQTT" version: "v2.3.1+" description: | - "Client implementation of the MQTT 3.1.1 specification for embedded devices.\n" + "Client implementation of the MQTT 5.0 specification for embedded devices.\n" license: "MIT" diff --git a/mqttFilePaths.cmake b/mqttFilePaths.cmake index 90c942b7d..ffd854db0 100644 --- a/mqttFilePaths.cmake +++ b/mqttFilePaths.cmake @@ -8,11 +8,12 @@ # MQTT library source files. set( MQTT_SOURCES "${CMAKE_CURRENT_LIST_DIR}/source/core_mqtt.c" - "${CMAKE_CURRENT_LIST_DIR}/source/core_mqtt_state.c" ) + "${CMAKE_CURRENT_LIST_DIR}/source/core_mqtt_state.c") # MQTT Serializer library source files. set( MQTT_SERIALIZER_SOURCES - "${CMAKE_CURRENT_LIST_DIR}/source/core_mqtt_serializer.c" ) + "${CMAKE_CURRENT_LIST_DIR}/source/core_mqtt_serializer.c" + "${CMAKE_CURRENT_LIST_DIR}/source/core_mqtt_utils.c" ) # MQTT library Public Include directories. set( MQTT_INCLUDE_PUBLIC_DIRS diff --git a/source/core_mqtt.c b/source/core_mqtt.c index a823abf20..eede926ee 100644 --- a/source/core_mqtt.c +++ b/source/core_mqtt.c @@ -31,26 +31,11 @@ #include "core_mqtt.h" #include "core_mqtt_state.h" +#include "core_mqtt_utils.h" /* Include config defaults header to get default values of configs. */ #include "core_mqtt_config_defaults.h" -#ifndef MQTT_PRE_SEND_HOOK - -/** - * @brief Hook called before a 'send' operation is executed. - */ - #define MQTT_PRE_SEND_HOOK( pContext ) -#endif /* !MQTT_PRE_SEND_HOOK */ - -#ifndef MQTT_POST_SEND_HOOK - -/** - * @brief Hook called after the 'send' operation is complete. - */ - #define MQTT_POST_SEND_HOOK( pContext ) -#endif /* !MQTT_POST_SEND_HOOK */ - #ifndef MQTT_PRE_STATE_UPDATE_HOOK /** @@ -78,7 +63,7 @@ * @brief Number of vectors required to encode one topic filter in a subscribe * request. Three vectors are required as there are three fields in the * subscribe request namely: - * 1. Topic filter length; 2. Topic filter; and 3. QoS in this order. + * 1. Topic filter length; 2. Topic filter; and 3. Subscription options in this order. */ #define CORE_MQTT_SUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ( 3U ) @@ -90,6 +75,11 @@ */ #define CORE_MQTT_UNSUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ( 2U ) +/** + * @brief Per the MQTT spec, the max packet size can be of max remaining length + 5 bytes + */ +#define MQTT_MAX_PACKET_SIZE ( 268435460U ) + struct MQTTVec { TransportOutVector_t * pVector; /**< Pointer to transport vector. USER SHOULD NOT ACCESS THIS DIRECTLY - IT IS AN INTERNAL DETAIL AND CAN CHANGE. */ @@ -128,7 +118,8 @@ static int32_t sendBuffer( MQTTContext_t * pContext, * @brief param[in] pWillInfo Last Will and Testament. Pass NULL if Last Will and * Testament is not used. * @brief param[in] remainingLength the length of the connect packet. - * + * @brief param[in] pPropertyBuilder Property builder containing CONNECT properties. + * @brief param[in] pWillPropertyBuilder Property builder containing WILL properties. * @note This operation may call the transport send function * repeatedly to send bytes over the network until either: * 1. The requested number of bytes @a remainingLength have been sent. @@ -143,7 +134,9 @@ static int32_t sendBuffer( MQTTContext_t * pContext, static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, - size_t remainingLength ); + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder, + const MQTTPropBuilder_t * pWillPropertyBuilder ); /** * @brief Sends the vector array passed through the parameters over the network. @@ -193,42 +186,6 @@ static size_t addEncodedStringToVector( uint8_t serializedLength[ CORE_MQTT_SERI TransportOutVector_t * iterator, size_t * updatedLength ); -/** - * @brief Send MQTT SUBSCRIBE message without copying the user data into a buffer and - * directly sending it. - * - * @param[in] pContext Initialized MQTT context. - * @param[in] pSubscriptionList List of MQTT subscription info. - * @param[in] subscriptionCount The count of elements in the list. - * @param[in] packetId The packet ID of the subscribe packet - * @param[in] remainingLength The remaining length of the subscribe packet. - * - * @return #MQTTSuccess or #MQTTSendFailed. - */ -static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext, - const MQTTSubscribeInfo_t * pSubscriptionList, - size_t subscriptionCount, - uint16_t packetId, - size_t remainingLength ); - -/** - * @brief Send MQTT UNSUBSCRIBE message without copying the user data into a buffer and - * directly sending it. - * - * @param[in] pContext Initialized MQTT context. - * @param[in] pSubscriptionList MQTT subscription info. - * @param[in] subscriptionCount The count of elements in the list. - * @param[in] packetId The packet ID of the unsubscribe packet. - * @param[in] remainingLength The remaining length of the unsubscribe packet. - * - * @return #MQTTSuccess or #MQTTSendFailed. - */ -static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext, - const MQTTSubscribeInfo_t * pSubscriptionList, - size_t subscriptionCount, - uint16_t packetId, - size_t remainingLength ); - /** * @brief Calculate the interval between two millisecond timestamps, including * when the later value has overflowed. @@ -406,6 +363,7 @@ static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext, * @param[in] pSubscriptionList List of MQTT subscription info. * @param[in] subscriptionCount The number of elements in pSubscriptionList. * @param[in] packetId Packet identifier. + * @param[in] subscriptionType Either #MQTT_TYPE_SUBSCRIBE or #MQTT_TYPE_UNSUBSCRIBE. * * @return #MQTTBadParameter if invalid parameters are passed; * #MQTTSuccess otherwise. @@ -413,7 +371,8 @@ static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext, static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, - uint16_t packetId ); + uint16_t packetId, + MQTTSubscriptionType_t subscriptionType ); /** * @brief Receives a CONNACK MQTT packet. @@ -459,12 +418,13 @@ static MQTTStatus_t handleCleanSession( MQTTContext_t * pContext ); * @brief Send the publish packet without copying the topic string and payload in * the buffer. * - * @brief param[in] pContext Initialized MQTT context. - * @brief param[in] pPublishInfo MQTT PUBLISH packet parameters. - * @brief param[in] pMqttHeader the serialized MQTT header with the header byte; + * @param[in] pContext Initialized MQTT context. + * @param[in] pPublishInfo MQTT PUBLISH packet parameters. + * @param[in] pMqttHeader the serialized MQTT header with the header byte; * the encoded length of the packet; and the encoded length of the topic string. - * @brief param[in] headerSize Size of the serialized PUBLISH header. - * @brief param[in] packetId Packet Id of the publish packet. + * @param[in] headerSize Size of the serialized PUBLISH header. + * @param[in] packetId Packet Id of the publish packet. + * @param[in] pPropertyBuilder MQTT Publish property builder. * * @return #MQTTSendFailed if transport send during resend failed; * #MQTTSuccess otherwise. @@ -473,14 +433,15 @@ static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, uint8_t * pMqttHeader, size_t headerSize, - uint16_t packetId ); + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ); /** * @brief Function to validate #MQTT_Publish parameters. * - * @brief param[in] pContext Initialized MQTT context. - * @brief param[in] pPublishInfo MQTT PUBLISH packet parameters. - * @brief param[in] packetId Packet Id for the MQTT PUBLISH packet. + * @param[in] pContext Initialized MQTT context. + * @param[in] pPublishInfo MQTT PUBLISH packet parameters. + * @param[in] packetId Packet Id for the MQTT PUBLISH packet. * * @return #MQTTBadParameter if invalid parameters are passed; * #MQTTSuccess otherwise. @@ -558,6 +519,197 @@ static bool matchTopicFilter( const char * pTopicName, const char * pTopicFilter, uint16_t topicFilterLength ); + + +/** + * @brief Send acks for received QoS 1/2 publishes with properties. + * + * @param[in] pContext MQTT Connection context. + * @param[in] packetId packet ID of original PUBLISH. + * @param[in] publishState Current publish state in record. + * @param[in] reasonCode Reason code to be sent in the Publish Ack. + * + * @return #MQTTSuccess, #MQTTBadParameter, #MQTTIllegalState or #MQTTSendFailed. + */ +static MQTTStatus_t sendPublishAcksWithProperty( MQTTContext_t * pContext, + uint16_t packetId, + MQTTPublishState_t publishState, + MQTTSuccessFailReasonCode_t reasonCode ); + +/** + * @brief Send the disconnect packet without copying the reason code and properties in + * the buffer. + * + * @param[in] pContext MQTT Connection context. + * @param[in] reasonCode Reason code to be sent in the Disconnect packet. + * @param[in] remainingLength Remaining length of the packet. + * @param[in] pPropertyBuilder MQTT Disconnect property builder. + * + * + * @return #MQTTSendFailed if transport send during resend failed; + * #MQTTSuccess otherwise. + */ + +static MQTTStatus_t sendDisconnectWithoutCopy( MQTTContext_t * pContext, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder ); + +/** + * @brief Initialize an MQTTConnectProperties_t. + * + * @note This function initializes the connect properties to default values. + * + * @param[in] pConnectProperties The connect properties to initialize. + * + * @return #MQTTSuccess + */ +static MQTTStatus_t initConnectProperties( MQTTConnectProperties_t * pConnectProperties ); + +/** + * @brief Validate Publish Ack Reason Code + * + * @param[in] reasonCode Reason Code to validate + * + * @return #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise + */ +static MQTTStatus_t validatePublishAckReasonCode( MQTTSuccessFailReasonCode_t reasonCode ); + +/** + * @brief Handle Incoming Subscribe ACK + * + * @param[in] pContext MQTT Connection context. + * @param[in] pIncomingPacket Information of incoming packet + * + * @return #MQTTSuccess or #MQTTBadResponse + */ +static MQTTStatus_t handleSuback( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ); + +/** + * @brief Handle Incoming Disconnect + * + * @param[in] pContext MQTT Connection context. + * @param[in] pIncomingPacket Information of incoming packet + * + * @return #MQTTSuccess or #MQTTBadResponse + */ +static MQTTStatus_t handleIncomingDisconnect( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ); + +/** + * @brief Validate Shared Subscriptions + * + * @param[in] pContext MQTT Connection context. + * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] iterator The iterator pointing to a topic filter in pSubscriptionList. + * + * @return #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise + * */ +static MQTTStatus_t validateSharedSubscriptions( const MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + const size_t iterator ); + + +/** + * @brief Send Subscribe without copying the users data into any buffer. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] subscriptionCount Number of elements in pSubscriptionList. + * @param[in] packetId Packet identifier. + * @param[in] remainingLength Remaining length of the packet. + * @param[in] pPropertyBuilder MQTT property builder. + * @note This operation may call the transport send function + * repeatedly to send bytes over the network until either: + * 1. The requested number of bytes @a remainingLength have been sent. + * OR + * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this + * function. + * OR + * 3. There is an error in sending data over the network. + * + * @return #MQTTSendFailed or #MQTTSuccess. + */ + +static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId, + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder ); + +/** + * @brief Send Unsubscribe without copying the users data into any buffer. + * + * @param[in] pContext Initialized MQTT context. + * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] subscriptionCount Number of elements in pSubscriptionList. + * @param[in] packetId Packet identifier. + * @param[in] remainingLength Remaining length of the packet. + * @param[in] pPropertyBuilder MQTT property builder. + * @note This operation may call the transport send function + * repeatedly to send bytes over the network until either: + * 1. The requested number of bytes @a remainingLength have been sent. + * OR + * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this + * function. + * OR + * 3. There is an error in sending data over the network. + * + * @return #MQTTSendFailed or #MQTTSuccess. + */ +static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + uint16_t packetId, + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder ); + +/** + * @brief Add subscription options to the options array. + * + * @param[in] pSubscriptionInfo MQTT subscription information. + * @param[out] subscriptionOptionsArray Array to store subscription options. + * @param[in] currentOptionIndex Current index in the options array. + * + * @note This function does not return a status as it performs a direct array update. + */ +static void addSubscriptionOptions( const MQTTSubscribeInfo_t pSubscriptionInfo, + uint8_t * subscriptionOptionsArray, + size_t currentOptionIndex ); + +/** + * @brief Check if wildcard subscriptions are allowed and valid. + * + * @param[in] isWildcardAvailable Flag indicating if wildcard subscriptions are supported. + * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] iterator The iterator pointing to a topic filter in pSubscriptionList. + * + * @return true if wildcard subscriptions are valid or not present; + * false if wildcards are used but not supported + */ +static bool checkWildcardSubscriptions( uint8_t isWildcardAvailable, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t iterator ); + +/** + * @brief Validate the topic filter in a subscription. + * + * @param[in] pContext MQTT Connection context. + * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] iterator The iterator pointing to a topic filter in pSubscriptionList. + * + * @return Returns one of the following: + * - #MQTTSuccess if the topic filter is valid + * - #MQTTBadParameter if the topic filter is invalid or parameters are NULL + */ +static MQTTStatus_t validateSubscribeTopicFilter( const MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t iterator ); + /*-----------------------------------------------------------*/ static bool matchEndWildcardsSpecialCases( const char * pTopicFilter, @@ -645,7 +797,7 @@ static bool matchWildcards( const char * pTopicName, break; } - nameIndex += 1; + nameIndex += 1U; } /* Determine if the topic filter contains a child level after the current level @@ -681,7 +833,7 @@ static bool matchWildcards( const char * pTopicName, * reached past the end of the topic name, and thus, we decrement the * index to the last character in the topic name.*/ /* coverity[integer_overflow] */ - nameIndex -= 1; + nameIndex -= 1U; } } @@ -881,6 +1033,8 @@ static int32_t sendMessageVector( MQTTContext_t * pContext, return bytesSentOrError; } +/*-----------------------------------------------------------*/ + static int32_t sendBuffer( MQTTContext_t * pContext, const uint8_t * pBufferToSend, size_t bytesToSend ) @@ -1441,15 +1595,18 @@ static MQTTStatus_t handleIncomingPublish( MQTTContext_t * pContext, MQTTStatus_t status; MQTTPublishState_t publishRecordState = MQTTStateNull; uint16_t packetIdentifier = 0U; - MQTTPublishInfo_t publishInfo; + MQTTPublishInfo_t publishInfo = { 0 }; MQTTDeserializedInfo_t deserializedInfo; bool duplicatePublish = false; + MQTTPropBuilder_t propBuffer = { 0 }; + MQTTSuccessFailReasonCode_t reasonCode; + bool ackPropsAdded; assert( pContext != NULL ); assert( pIncomingPacket != NULL ); assert( pContext->appCallback != NULL ); - status = MQTT_DeserializePublish( pIncomingPacket, &packetIdentifier, &publishInfo ); + status = MQTT_DeserializePublish( pIncomingPacket, &packetIdentifier, &publishInfo, &propBuffer, pContext->connectProperties.maxPacketSize, pContext->connectProperties.topicAliasMax ); LogInfo( ( "De-serialized incoming PUBLISH packet: DeserializerResult=%s.", MQTT_Status_strerror( status ) ) ); @@ -1537,26 +1694,33 @@ static MQTTStatus_t handleIncomingPublish( MQTTContext_t * pContext, if( status == MQTTSuccess ) { - /* Set fields of deserialized struct. */ deserializedInfo.packetIdentifier = packetIdentifier; deserializedInfo.pPublishInfo = &publishInfo; deserializedInfo.deserializationResult = status; /* Invoke application callback to hand the buffer over to application - * before sending acks. - * Application callback will be invoked for all publishes, except for - * duplicate incoming publishes. */ + * before sending acks. */ + + reasonCode = MQTT_INVALID_REASON_CODE; + if( duplicatePublish == false ) { - pContext->appCallback( pContext, - pIncomingPacket, - &deserializedInfo ); + pContext->appCallback( pContext, pIncomingPacket, &deserializedInfo, &reasonCode, &pContext->ackPropsBuffer, &propBuffer ); } - /* Send PUBACK or PUBREC if necessary. */ - status = sendPublishAcks( pContext, - packetIdentifier, - publishRecordState ); + /* Send PUBREC or PUBCOMP if necessary. */ + ackPropsAdded = ( pContext->ackPropsBuffer.pBuffer != NULL ) && ( pContext->ackPropsBuffer.currentIndex > 0U ); + + if( ( ackPropsAdded == false ) && ( reasonCode == MQTT_INVALID_REASON_CODE ) ) + { + status = sendPublishAcks( pContext, + packetIdentifier, + publishRecordState ); + } + else + { + status = sendPublishAcksWithProperty( pContext, packetIdentifier, publishRecordState, reasonCode ); + } } return status; @@ -1573,6 +1737,11 @@ static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext, MQTTPubAckType_t ackType; MQTTEventCallback_t appCallback; MQTTDeserializedInfo_t deserializedInfo; + MQTTPropBuilder_t propBuffer = { 0 }; + MQTTSuccessFailReasonCode_t reasonCode; + bool ackPropsAdded; + + MQTTReasonCodeInfo_t incomingReasonCode = { 0 }; assert( pContext != NULL ); assert( pIncomingPacket != NULL ); @@ -1581,7 +1750,9 @@ static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext, appCallback = pContext->appCallback; ackType = getAckFromPacketType( pIncomingPacket->type ); - status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL ); + + status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL, &incomingReasonCode, pContext->connectProperties.requestProblemInfo, pContext->connectProperties.maxPacketSize, &propBuffer, NULL ); + LogInfo( ( "Ack packet deserialized with result: %s.", MQTT_Status_strerror( status ) ) ); @@ -1622,19 +1793,32 @@ static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext, if( status == MQTTSuccess ) { - /* Set fields of deserialized struct. */ deserializedInfo.packetIdentifier = packetIdentifier; deserializedInfo.deserializationResult = status; deserializedInfo.pPublishInfo = NULL; + deserializedInfo.pReasonCode = &incomingReasonCode; + /* Invoke application callback to hand the buffer over to application * before sending acks. */ - appCallback( pContext, pIncomingPacket, &deserializedInfo ); + + reasonCode = MQTT_INVALID_REASON_CODE; + + appCallback( pContext, pIncomingPacket, &deserializedInfo, &reasonCode, &pContext->ackPropsBuffer, &propBuffer ); /* Send PUBREL or PUBCOMP if necessary. */ - status = sendPublishAcks( pContext, - packetIdentifier, - publishRecordState ); + ackPropsAdded = ( ( pContext->ackPropsBuffer.pBuffer != NULL ) && ( pContext->ackPropsBuffer.currentIndex > 0U ) ); + + if( ( ackPropsAdded == false ) && ( reasonCode == MQTT_INVALID_REASON_CODE ) ) + { + status = sendPublishAcks( pContext, + packetIdentifier, + publishRecordState ); + } + else + { + status = sendPublishAcksWithProperty( pContext, packetIdentifier, publishRecordState, reasonCode ); + } } return status; @@ -1680,7 +1864,7 @@ static MQTTStatus_t handleIncomingAck( MQTTContext_t * pContext, break; case MQTT_PACKET_TYPE_PINGRESP: - status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL ); + status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL, NULL, 0, pContext->connectProperties.maxPacketSize, NULL, NULL ); invokeAppCallback = ( status == MQTTSuccess ) && !manageKeepAlive; if( ( status == MQTTSuccess ) && ( manageKeepAlive == true ) ) @@ -1693,8 +1877,7 @@ static MQTTStatus_t handleIncomingAck( MQTTContext_t * pContext, case MQTT_PACKET_TYPE_SUBACK: case MQTT_PACKET_TYPE_UNSUBACK: /* Deserialize and give these to the app provided callback. */ - status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL ); - invokeAppCallback = ( status == MQTTSuccess ) || ( status == MQTTServerRefused ); + status = handleSuback( pContext, pIncomingPacket ); break; default: @@ -1711,13 +1894,14 @@ static MQTTStatus_t handleIncomingAck( MQTTContext_t * pContext, deserializedInfo.packetIdentifier = packetIdentifier; deserializedInfo.deserializationResult = status; deserializedInfo.pPublishInfo = NULL; - appCallback( pContext, pIncomingPacket, &deserializedInfo ); + appCallback( pContext, pIncomingPacket, &deserializedInfo, NULL, &pContext->ackPropsBuffer, NULL ); /* In case a SUBACK indicated refusal, reset the status to continue the loop. */ status = MQTTSuccess; } return status; } + /*-----------------------------------------------------------*/ static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext, @@ -1837,6 +2021,22 @@ static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext, { status = handleIncomingPublish( pContext, &incomingPacket ); } + + else if( ( incomingPacket.type == MQTT_PACKET_TYPE_DISCONNECT ) ) + { + status = handleIncomingDisconnect( pContext, &incomingPacket ); + + if( status == MQTTSuccess ) + { + LogInfo( ( "Disconnected from the broker." ) ); + pContext->connectStatus = MQTTNotConnected; + + /* Reset the index and clean the buffer on a successful disconnect. */ + pContext->index = 0; + ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size ); + } + } + else { status = handleIncomingAck( pContext, &incomingPacket, manageKeepAlive ); @@ -1871,7 +2071,8 @@ static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext, static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, - uint16_t packetId ) + uint16_t packetId, + MQTTSubscriptionType_t subscriptionType ) { MQTTStatus_t status = MQTTSuccess; size_t iterator; @@ -1899,7 +2100,7 @@ static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pC { if( pContext->incomingPublishRecords == NULL ) { - for( iterator = 0; iterator < subscriptionCount; iterator++ ) + for( iterator = 0U; iterator < subscriptionCount; iterator++ ) { if( pSubscriptionList[ iterator ].qos > MQTTQoS0 ) { @@ -1912,6 +2113,14 @@ static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pC } } } + + if( ( status == MQTTSuccess ) && ( subscriptionType == MQTT_TYPE_SUBSCRIBE ) ) + { + for( iterator = 0U; iterator < subscriptionCount; iterator++ ) + { + status = validateSubscribeTopicFilter( pContext, pSubscriptionList, iterator ); + } + } } return status; @@ -1963,48 +2172,85 @@ static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId, - size_t remainingLength ) + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder ) { MQTTStatus_t status = MQTTSuccess; uint8_t * pIndex; + + /** + * Fixed Size Properties + */ TransportOutVector_t pIoVector[ MQTT_SUB_UNSUB_MAX_VECTORS ]; TransportOutVector_t * pIterator; uint8_t serializedTopicFieldLength[ MQTT_SUB_UNSUB_MAX_VECTORS ][ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ]; + uint8_t subscriptionOptionsArray[ MQTT_SUB_UNSUB_MAX_VECTORS / CORE_MQTT_SUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ]; size_t totalPacketLength = 0U; size_t ioVectorLength = 0U; size_t subscriptionsSent = 0U; - size_t vectorsAdded; + size_t vectorsAdded = 0U; size_t topicFieldLengthIndex; + size_t subscribePropLen = 0; + size_t currentOptionIndex = 0U; + + /** + * Maximum number of bytes by the fixed header of a SUBSCRIBE packet. + * MQTT Control Byte 0 + 1 = 1 + * Remaining Length + 4 = 5 + * Packet Id + 2 = 7 + */ + uint8_t subscribeHeader[ 7U ]; - /* Maximum number of bytes required by the 'fixed' part of the SUBSCRIBE - * packet header according to the MQTT specification. - * MQTT Control Byte 0 + 1 = 1 - * Remaining length (max) + 4 = 5 - * Packet ID + 2 = 7 */ - uint8_t subscribeheader[ 7U ]; - - /* The vector array should be at least three element long as the topic - * string needs these many vector elements to be stored. */ - assert( MQTT_SUB_UNSUB_MAX_VECTORS >= CORE_MQTT_SUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ); + /** + * Maximum number of bytes to send the Property Length. + * Property Length 0 + 4 = 4 + */ + uint8_t propertyLength[ 4U ]; - pIndex = subscribeheader; + pIndex = subscribeHeader; pIterator = pIoVector; - pIndex = MQTT_SerializeSubscribeHeader( remainingLength, - pIndex, - packetId ); + pIndex = MQTT_SerializeSubscribeHeader( remainingLength, pIndex, packetId ); + + pIterator->iov_base = subscribeHeader; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pIterator->iov_len = ( size_t ) ( pIndex - subscribeHeader ); + totalPacketLength += pIterator->iov_len; + pIterator++; + ioVectorLength++; + + /** + * Sending Property Buffer + */ + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + subscribePropLen = pPropertyBuilder->currentIndex; + } - /* The header is to be sent first. */ - pIterator->iov_base = subscribeheader; + pIndex = propertyLength; + pIndex = encodeVariableLength( propertyLength, subscribePropLen ); + pIterator->iov_base = propertyLength; /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ /* coverity[misra_c_2012_rule_18_2_violation] */ /* coverity[misra_c_2012_rule_10_8_violation] */ - pIterator->iov_len = ( size_t ) ( pIndex - subscribeheader ); + pIterator->iov_len = ( size_t ) ( pIndex - propertyLength ); totalPacketLength += pIterator->iov_len; pIterator++; ioVectorLength++; + if( subscribePropLen > 0U ) + { + pIterator->iov_base = pPropertyBuilder->pBuffer; + pIterator->iov_len = pPropertyBuilder->currentIndex; + totalPacketLength += pIterator->iov_len; + pIterator++; + ioVectorLength++; + } + while( ( status == MQTTSuccess ) && ( subscriptionsSent < subscriptionCount ) ) { /* Reset the index for next iteration. */ @@ -2015,7 +2261,7 @@ static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext, while( ( ioVectorLength <= ( MQTT_SUB_UNSUB_MAX_VECTORS - CORE_MQTT_SUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ) ) && ( subscriptionsSent < subscriptionCount ) ) { - /* The topic filter and the filter length gets sent next. */ + /* The topic filter and the filter length gets sent next. (filter length - 2 bytes , topic filter - utf - 8 ) */ vectorsAdded = addEncodedStringToVector( serializedTopicFieldLength[ topicFieldLengthIndex ], pSubscriptionList[ subscriptionsSent ].pTopicFilter, pSubscriptionList[ subscriptionsSent ].topicFilterLength, @@ -2024,29 +2270,26 @@ static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext, /* Update the pointer after the above operation. */ pIterator = &pIterator[ vectorsAdded ]; + ioVectorLength += vectorsAdded; - /* Lastly, the QoS gets sent. */ - pIterator->iov_base = &( pSubscriptionList[ subscriptionsSent ].qos ); - pIterator->iov_len = 1U; - totalPacketLength += pIterator->iov_len; + /* Lastly, send the subscription options. */ - /* Increment the pointer. */ - pIterator++; + addSubscriptionOptions( pSubscriptionList[ subscriptionsSent ], subscriptionOptionsArray, currentOptionIndex ); - /* Two slots get used by the topic string length and topic string. - * One slot gets used by the quality of service. */ - ioVectorLength += vectorsAdded + 1U; + pIterator->iov_base = &( subscriptionOptionsArray[ currentOptionIndex ] ); + pIterator->iov_len = 1U; + totalPacketLength += 1U; + pIterator++; + ioVectorLength++; + currentOptionIndex++; subscriptionsSent++; - - /* The index needs to be updated for next iteration. */ topicFieldLengthIndex++; } - if( sendMessageVector( pContext, - pIoVector, - ioVectorLength ) != ( int32_t ) totalPacketLength ) + if( sendMessageVector( pContext, pIoVector, ioVectorLength ) != ( int32_t ) totalPacketLength ) { + LogError( ( "Error in sending SUBSCRIBE/UNSUBSCRIBE packet" ) ); status = MQTTSendFailed; } @@ -2067,65 +2310,101 @@ static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId, - size_t remainingLength ) + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder ) { MQTTStatus_t status = MQTTSuccess; uint8_t * pIndex; + + /** + * Fixed Size Properties + */ TransportOutVector_t pIoVector[ MQTT_SUB_UNSUB_MAX_VECTORS ]; TransportOutVector_t * pIterator; uint8_t serializedTopicFieldLength[ MQTT_SUB_UNSUB_MAX_VECTORS ][ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ]; size_t totalPacketLength = 0U; - size_t unsubscriptionsSent = 0U; size_t ioVectorLength = 0U; - size_t vectorsAdded; + size_t unsubscriptionsSent = 0U; + size_t vectorsAdded = 0U; size_t topicFieldLengthIndex; + size_t unsubscribePropLen = 0U; - /* Maximum number of bytes required by the 'fixed' part of the UNSUBSCRIBE - * packet header according to the MQTT specification. - * MQTT Control Byte 0 + 1 = 1 - * Remaining length (max) + 4 = 5 - * Packet ID + 2 = 7 */ - uint8_t unsubscribeheader[ 7U ]; + /** + * Maximum number of bytes by the fixed header of a SUBSCRIBE packet. + * MQTT Control Byte 0 + 1 = 1 + * Remaining Length + 4 = 5 + * Packet Id + 2 = 7 + */ + uint8_t unsubscribeHeader[ 7U ]; - /* The vector array should be at least three element long as the topic - * string needs these many vector elements to be stored. */ - assert( MQTT_SUB_UNSUB_MAX_VECTORS >= CORE_MQTT_UNSUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ); + /** + * Maximum number of bytes to send the Property Length. + * Property Length 0 + 4 = 4 + */ + uint8_t propertyLength[ 4U ]; - pIndex = unsubscribeheader; + pIndex = unsubscribeHeader; pIterator = pIoVector; - pIndex = MQTT_SerializeUnsubscribeHeader( remainingLength, - pIndex, - packetId ); + pIndex = MQTT_SerializeUnsubscribeHeader( remainingLength, pIndex, packetId ); + + pIterator->iov_base = unsubscribeHeader; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pIterator->iov_len = ( size_t ) ( pIndex - unsubscribeHeader ); + totalPacketLength += pIterator->iov_len; + pIterator++; + ioVectorLength++; + + /** + * Sending Property Buffer + */ + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + unsubscribePropLen = pPropertyBuilder->currentIndex; + } - /* The header is to be sent first. */ - pIterator->iov_base = unsubscribeheader; + pIndex = propertyLength; + pIndex = encodeVariableLength( propertyLength, unsubscribePropLen ); + pIterator->iov_base = propertyLength; /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ /* coverity[misra_c_2012_rule_18_2_violation] */ /* coverity[misra_c_2012_rule_10_8_violation] */ - pIterator->iov_len = ( size_t ) ( pIndex - unsubscribeheader ); + pIterator->iov_len = ( size_t ) ( pIndex - propertyLength ); totalPacketLength += pIterator->iov_len; pIterator++; ioVectorLength++; + if( unsubscribePropLen > 0U ) + { + pIterator->iov_base = pPropertyBuilder->pBuffer; + pIterator->iov_len = pPropertyBuilder->currentIndex; + totalPacketLength += pIterator->iov_len; + pIterator++; + ioVectorLength++; + } + while( ( status == MQTTSuccess ) && ( unsubscriptionsSent < subscriptionCount ) ) { /* Reset the index for next iteration. */ topicFieldLengthIndex = 0; - /* Check whether the subscription topic will fit in the given vector. */ + /* Check whether the subscription topic (with QoS) will fit in the + * given vector. */ while( ( ioVectorLength <= ( MQTT_SUB_UNSUB_MAX_VECTORS - CORE_MQTT_UNSUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ) ) && ( unsubscriptionsSent < subscriptionCount ) ) { - /* The topic filter gets sent next. */ + /* The topic filter and the filter length gets sent next. (filter length - 2 bytes , topic filter - utf - 8 ) */ vectorsAdded = addEncodedStringToVector( serializedTopicFieldLength[ topicFieldLengthIndex ], pSubscriptionList[ unsubscriptionsSent ].pTopicFilter, pSubscriptionList[ unsubscriptionsSent ].topicFilterLength, pIterator, &totalPacketLength ); - /* Update the iterator to point to the next empty location. */ + /* Update the pointer after the above operation. */ pIterator = &pIterator[ vectorsAdded ]; /* Update the total count based on how many vectors were added. */ ioVectorLength += vectorsAdded; @@ -2138,6 +2417,7 @@ static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext, if( sendMessageVector( pContext, pIoVector, ioVectorLength ) != ( int32_t ) totalPacketLength ) { + LogError( ( "Error in sending UNSUBSCRIBE packet" ) ); status = MQTTSendFailed; } @@ -2158,24 +2438,37 @@ static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, uint8_t * pMqttHeader, size_t headerSize, - uint16_t packetId ) + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ) { MQTTStatus_t status = MQTTSuccess; size_t ioVectorLength; size_t totalMessageLength; + size_t publishPropLength = 0; bool dupFlagChanged = false; /* Bytes required to encode the packet ID in an MQTT header according to * the MQTT specification. */ uint8_t serializedPacketID[ 2U ]; + /** + * Maximum number of bytes to send the Property Length. + * Property Length 0 + 4 = 4 + */ + uint8_t propertyLength[ 4U ]; + /* Maximum number of vectors required to encode and send a publish * packet. The breakdown is shown below. * Fixed header (including topic string length) 0 + 1 = 1 * Topic string + 1 = 2 * Packet ID (only when QoS > QoS0) + 1 = 3 - * Payload + 1 = 4 */ - TransportOutVector_t pIoVector[ 4U ]; + * Property Length + 1 = 4 + * Optional Properties + 1 = 5 + * Payload + 1 = 6 */ + + TransportOutVector_t pIoVector[ 6U ]; + uint8_t * pIndex; + TransportOutVector_t * iterator; /* The header is sent first. */ pIoVector[ 0U ].iov_base = pMqttHeader; @@ -2204,6 +2497,35 @@ static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext, totalMessageLength += sizeof( serializedPacketID ); } + /*Serialize the fixed publish properties.*/ + + if( pPropertyBuilder != NULL ) + { + publishPropLength = pPropertyBuilder->currentIndex; + } + + iterator = &pIoVector[ ioVectorLength ]; + pIndex = propertyLength; + pIndex = encodeVariableLength( pIndex, publishPropLength ); + iterator->iov_base = propertyLength; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - propertyLength ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + if( pPropertyBuilder != NULL ) + { + iterator->iov_base = pPropertyBuilder->pBuffer; + iterator->iov_len = pPropertyBuilder->currentIndex; + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + } + /* Publish packets are allowed to contain no payload. */ if( pPublishInfo->payloadLength > 0U ) { @@ -2263,41 +2585,58 @@ static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext, static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, - size_t remainingLength ) + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder, + const MQTTPropBuilder_t * pWillPropertyBuilder ) { MQTTStatus_t status = MQTTSuccess; TransportOutVector_t * iterator; size_t ioVectorLength = 0U; size_t totalMessageLength = 0U; + size_t connectPropLen = 0U; int32_t bytesSentOrError; uint8_t * pIndex; - uint8_t serializedClientIDLength[ 2 ]; - uint8_t serializedTopicLength[ 2 ]; - uint8_t serializedPayloadLength[ 2 ]; - uint8_t serializedUsernameLength[ 2 ]; - uint8_t serializedPasswordLength[ 2 ]; + uint8_t serializedClientIDLength[ 2U ]; + uint8_t serializedTopicLength[ 2U ]; + uint8_t serializedPayloadLength[ 2U ]; + uint8_t serializedUsernameLength[ 2U ]; + uint8_t serializedPasswordLength[ 2U ]; + + /** + * Maximum number of bytes to send the Property Length. + * Property Length 0 + 4 = 4 + */ + uint8_t propertyLength[ 4U ]; + uint8_t willPropertyLength[ 4U ]; size_t vectorsAdded; - /* Maximum number of bytes required by the 'fixed' part of the CONNECT + + + /* Maximum number of bytes required by the fixed part of the CONNECT * packet header according to the MQTT specification. - * MQTT Control Byte 0 + 1 = 1 - * Remaining length (max) + 4 = 5 - * Protocol Name Length + 2 = 7 - * Protocol Name (MQTT) + 4 = 11 - * Protocol level + 1 = 12 - * Connect flags + 1 = 13 - * Keep alive + 2 = 15 */ + * MQTT Control Byte 0 + 1 = 1 + * Remaining length (max) + 4 = 5 + * Protocol Name Length + 2 = 7 + * Protocol Name (MQTT) + 4 = 11 + * Protocol level + 1 = 12 + * Connect flags + 1 = 13 + * Keep alive + 2 = 15 + */ + uint8_t connectPacketHeader[ 15U ]; /* The maximum vectors required to encode and send a connect packet. The * breakdown is shown below. * Fixed header 0 + 1 = 1 - * Client ID + 2 = 3 - * Will topic + 2 = 5 - * Will payload + 2 = 7 - * Username + 2 = 9 - * Password + 2 = 11 */ - TransportOutVector_t pIoVector[ 11U ]; + * Connect Properties + 2 = 3 + * Client ID + 2 = 5 + * Will Properties + 2 = 7 + * Will topic + 2 = 9 + * Will payload + 2 = 11 + * Username + 2 = 13 + * Password + 2 = 15 + */ + TransportOutVector_t pIoVector[ 15U ]; iterator = pIoVector; pIndex = connectPacketHeader; @@ -2315,9 +2654,9 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, pWillInfo, remainingLength ); - assert( ( ( size_t ) ( pIndex - connectPacketHeader ) ) <= sizeof( connectPacketHeader ) ); + /* Set Default value of serverKeepAlive to keepAlive value sent in the CONNECT packet. */ + pContext->connectProperties.serverKeepAlive = pConnectInfo->keepAliveSeconds; - /* The header gets sent first. */ iterator->iov_base = connectPacketHeader; /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ @@ -2328,6 +2667,42 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, iterator++; ioVectorLength++; + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + connectPropLen = pPropertyBuilder->currentIndex; + } + + pIndex = propertyLength; + pIndex = encodeVariableLength( propertyLength, connectPropLen ); + iterator->iov_base = propertyLength; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - propertyLength ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + if( connectPropLen > 0U ) + { + /*Serialize the will properties*/ + + iterator->iov_base = pPropertyBuilder->pBuffer; + iterator->iov_len = pPropertyBuilder->currentIndex; + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + } + + /* + * Updating Context with optional properties + */ + if( connectPropLen > 0U ) + { + status = updateContextWithConnectProps( pPropertyBuilder, &pContext->connectProperties ); + } + /* Serialize the client ID. */ vectorsAdded = addEncodedStringToVector( serializedClientIDLength, pConnectInfo->pClientIdentifier, @@ -2341,6 +2716,36 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, if( pWillInfo != NULL ) { + size_t willPropsLen = 0U; + + if( ( pWillPropertyBuilder != NULL ) && ( pWillPropertyBuilder->pBuffer != NULL ) ) + { + willPropsLen = pWillPropertyBuilder->currentIndex; + } + + pIndex = willPropertyLength; + pIndex = encodeVariableLength( willPropertyLength, willPropsLen ); + iterator->iov_base = willPropertyLength; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - willPropertyLength ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + if( willPropsLen > 0U ) + { + /*Serialize the will properties*/ + + iterator->iov_base = pWillPropertyBuilder->pBuffer; + iterator->iov_len = pWillPropertyBuilder->currentIndex; + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + } + /* Serialize the topic. */ vectorsAdded = addEncodedStringToVector( serializedTopicLength, pWillInfo->pTopicName, @@ -2352,7 +2757,6 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext, iterator = &iterator[ vectorsAdded ]; ioVectorLength += vectorsAdded; - /* Serialize the payload. Payload of last will and testament can be NULL. */ vectorsAdded = addEncodedStringToVector( serializedPayloadLength, pWillInfo->pPayload, @@ -2417,6 +2821,8 @@ static MQTTStatus_t receiveConnack( MQTTContext_t * pContext, uint32_t entryTimeMs = 0U, remainingTimeMs = 0U, timeTakenMs = 0U; bool breakFromLoop = false; uint16_t loopCount = 0U; + MQTTDeserializedInfo_t deserializedInfo; + MQTTPropBuilder_t propBuffer = { 0 }; assert( pContext != NULL ); assert( pIncomingPacket != NULL ); @@ -2500,7 +2906,7 @@ static MQTTStatus_t receiveConnack( MQTTContext_t * pContext, pIncomingPacket->pRemainingData = pContext->networkBuffer.pBuffer; /* Deserialize CONNACK. */ - status = MQTT_DeserializeAck( pIncomingPacket, NULL, pSessionPresent ); + status = MQTT_DeserializeAck( pIncomingPacket, NULL, pSessionPresent, NULL, 0, 0, &propBuffer, &pContext->connectProperties ); } /* If a clean session is requested, a session present should not be set by @@ -2525,6 +2931,12 @@ static MQTTStatus_t receiveConnack( MQTTContext_t * pContext, MQTT_Status_strerror( status ) ) ); } + if( ( status == MQTTSuccess ) || ( status == MQTTServerRefused ) ) + { + deserializedInfo.deserializationResult = status; + pContext->appCallback( pContext, pIncomingPacket, &deserializedInfo, NULL, &pContext->ackPropsBuffer, &propBuffer ); + } + return status; } @@ -2588,6 +3000,8 @@ static MQTTStatus_t handleUncleanSessionResumption( MQTTContext_t * pContext ) return status; } +/*-----------------------------------------------------------*/ + static MQTTStatus_t handleCleanSession( MQTTContext_t * pContext ) { MQTTStatus_t status = MQTTSuccess; @@ -2635,6 +3049,8 @@ static MQTTStatus_t handleCleanSession( MQTTContext_t * pContext ) return status; } +/*-----------------------------------------------------------*/ + static MQTTStatus_t validatePublishParams( const MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, uint16_t packetId ) @@ -2688,6 +3104,7 @@ MQTTStatus_t MQTT_Init( MQTTContext_t * pContext, const MQTTFixedBuffer_t * pNetworkBuffer ) { MQTTStatus_t status = MQTTSuccess; + MQTTConnectProperties_t connectProperties; /* Validate arguments. */ if( ( pContext == NULL ) || ( pTransportInterface == NULL ) || @@ -2730,9 +3147,14 @@ MQTTStatus_t MQTT_Init( MQTTContext_t * pContext, pContext->getTime = getTimeFunction; pContext->appCallback = userCallback; pContext->networkBuffer = *pNetworkBuffer; + pContext->ackPropsBuffer.pBuffer = NULL; /* Zero is not a valid packet ID per MQTT spec. Start from 1. */ pContext->nextPacketId = 1; + + /* Setting default connect properties in our application */ + status = initConnectProperties( &connectProperties ); + pContext->connectProperties = connectProperties; } return status; @@ -2744,7 +3166,9 @@ MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext, MQTTPubAckInfo_t * pOutgoingPublishRecords, size_t outgoingPublishCount, MQTTPubAckInfo_t * pIncomingPublishRecords, - size_t incomingPublishCount ) + size_t incomingPublishCount, + uint8_t * pBuffer, + size_t bufferLength ) { MQTTStatus_t status = MQTTSuccess; @@ -2790,6 +3214,22 @@ MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext, pContext->incomingPublishRecords = pIncomingPublishRecords; pContext->outgoingPublishRecordMaxCount = outgoingPublishCount; pContext->outgoingPublishRecords = pOutgoingPublishRecords; + + if( ( pBuffer != NULL ) && ( bufferLength != 0U ) ) + { + MQTTPropBuilder_t ackPropsBuffer; + status = MQTT_PropertyBuilder_Init( &ackPropsBuffer, pBuffer, bufferLength ); + + if( status == MQTTSuccess ) + { + pContext->ackPropsBuffer = ackPropsBuffer; + } + } + else + { + pContext->ackPropsBuffer.pBuffer = NULL; + pContext->ackPropsBuffer.bufferLength = 0; + } } return status; @@ -2914,7 +3354,9 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, uint32_t timeoutMs, - bool * pSessionPresent ) + bool * pSessionPresent, + const MQTTPropBuilder_t * pPropertyBuilder, + const MQTTPropBuilder_t * pWillPropertyBuilder ) { size_t remainingLength = 0UL, packetSize = 0UL; MQTTStatus_t status = MQTTSuccess; @@ -2923,6 +3365,11 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, incomingPacket.type = ( uint8_t ) 0; + if( ( pWillInfo != NULL ) && ( pWillPropertyBuilder != NULL ) ) + { + status = MQTT_ValidateWillProperties( pWillPropertyBuilder ); + } + if( ( pContext == NULL ) || ( pConnectInfo == NULL ) || ( pSessionPresent == NULL ) ) { LogError( ( "Argument cannot be NULL: pContext=%p, " @@ -2938,6 +3385,8 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, /* Get MQTT connect packet size and remaining length. */ status = MQTT_GetConnectPacketSize( pConnectInfo, pWillInfo, + pPropertyBuilder, + pWillPropertyBuilder, &remainingLength, &packetSize ); /* coverity[sensitive_data_leak] */ @@ -2962,10 +3411,13 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, status = sendConnectWithoutCopy( pContext, pConnectInfo, pWillInfo, - remainingLength ); + remainingLength, + pPropertyBuilder, + pWillPropertyBuilder ); } /* Read CONNACK from transport layer. */ + if( status == MQTTSuccess ) { status = receiveConnack( pContext, @@ -2975,6 +3427,22 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, pSessionPresent ); } + /** + * Update the maximum number of concurrent incoming and outgoing PUBLISH records + * based on MQTT 5.0 Receive Maximum property : + * + * - For incoming publishes: Use the minimum between the client's configured receive maximum (In the MQTT_Init function) + * and the receive maximum value sent in CONNECT properties + * + * - For outgoing publishes: Use the minimum between the client's configured maximum (In the MQTT_Init function) + * and the server's receive maximum value received in CONNACK properties + **/ + if( status == MQTTSuccess ) + { + pContext->incomingPublishRecordMaxCount = ( ( ( pContext->connectProperties.receiveMax ) < ( pContext->incomingPublishRecordMaxCount ) ) ? ( pContext->connectProperties.receiveMax ) : ( pContext->incomingPublishRecordMaxCount ) ); + pContext->outgoingPublishRecordMaxCount = ( ( ( pContext->connectProperties.serverReceiveMax ) < ( pContext->outgoingPublishRecordMaxCount ) ) ? ( pContext->connectProperties.serverReceiveMax ) : ( pContext->outgoingPublishRecordMaxCount ) ); + } + if( ( status == MQTTSuccess ) && ( *pSessionPresent != true ) ) { status = handleCleanSession( pContext ); @@ -2984,7 +3452,7 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, { pContext->connectStatus = MQTTConnected; /* Initialize keep-alive fields after a successful connection. */ - pContext->keepAliveIntervalSec = pConnectInfo->keepAliveSeconds; + pContext->keepAliveIntervalSec = pContext->connectProperties.serverKeepAlive; pContext->waitingForPingResp = false; pContext->pingReqSendTimeMs = 0U; } @@ -3041,25 +3509,37 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, - uint16_t packetId ) + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ) { MQTTConnectionStatus_t connectStatus; size_t remainingLength = 0UL, packetSize = 0UL; + MQTTStatus_t status = MQTTSuccess; - /* Validate arguments. */ - MQTTStatus_t status = validateSubscribeUnsubscribeParams( pContext, - pSubscriptionList, - subscriptionCount, - packetId ); + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + status = MQTT_ValidateSubscribeProperties( pContext->connectProperties.isSubscriptionIdAvailable, pPropertyBuilder ); + } + + if( status == MQTTSuccess ) + { + status = validateSubscribeUnsubscribeParams( pContext, + pSubscriptionList, + subscriptionCount, + packetId, + MQTT_TYPE_SUBSCRIBE ); + } if( status == MQTTSuccess ) { /* Get the remaining length and packet size.*/ status = MQTT_GetSubscribePacketSize( pSubscriptionList, subscriptionCount, + pPropertyBuilder, &remainingLength, - &packetSize ); - LogDebug( ( "SUBSCRIBE packet size is %lu and remaining length is %lu.", + &packetSize, + pContext->connectProperties.serverMaxPacketSize ); + LogError( ( "SUBSCRIBE packet size is %lu and remaining length is %lu.", ( unsigned long ) packetSize, ( unsigned long ) remainingLength ) ); } @@ -3082,7 +3562,8 @@ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, pSubscriptionList, subscriptionCount, packetId, - remainingLength ); + remainingLength, + pPropertyBuilder ); } MQTT_POST_STATE_UPDATE_HOOK( pContext ); @@ -3095,13 +3576,15 @@ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, - uint16_t packetId ) + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ) { size_t headerSize = 0UL; size_t remainingLength = 0UL; size_t packetSize = 0UL; MQTTPublishState_t publishStatus = MQTTStateNull; MQTTConnectionStatus_t connectStatus; + uint16_t topicAlias = 0U; /* Maximum number of bytes required by the 'fixed' part of the PUBLISH * packet header according to the MQTT specifications. @@ -3115,16 +3598,37 @@ MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, * an extra call to 'send' (in case writev is not defined) to send the * topic length. */ uint8_t mqttHeader[ 7U ]; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + status = MQTT_ValidatePublishProperties( pContext->connectProperties.serverTopicAliasMax, pPropertyBuilder, &topicAlias ); + } /* Validate arguments. */ - MQTTStatus_t status = validatePublishParams( pContext, pPublishInfo, packetId ); + if( status == MQTTSuccess ) + { + status = validatePublishParams( pContext, pPublishInfo, packetId ); + } if( status == MQTTSuccess ) { /* Get the remaining length and packet size.*/ - status = MQTT_GetPublishPacketSize( pPublishInfo, - &remainingLength, - &packetSize ); + + status = MQTT_ValidatePublishParams( pPublishInfo, + pContext->connectProperties.retainAvailable, + pContext->connectProperties.serverMaxQos, + topicAlias, + pContext->connectProperties.serverMaxPacketSize ); + + if( status == MQTTSuccess ) + { + status = MQTT_GetPublishPacketSize( pPublishInfo, + pPropertyBuilder, + &remainingLength, + &packetSize, + pContext->connectProperties.serverMaxPacketSize ); + } } if( status == MQTTSuccess ) @@ -3171,7 +3675,7 @@ MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, pPublishInfo, mqttHeader, headerSize, - packetId ); + packetId, pPropertyBuilder ); } if( ( status == MQTTSuccess ) && @@ -3306,27 +3810,40 @@ MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext ) MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, - uint16_t packetId ) + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ) { MQTTConnectionStatus_t connectStatus; size_t remainingLength = 0UL, packetSize = 0UL; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + status = MQTT_ValidateUnsubscribeProperties( pPropertyBuilder ); + } /* Validate arguments. */ - MQTTStatus_t status = validateSubscribeUnsubscribeParams( pContext, - pSubscriptionList, - subscriptionCount, - packetId ); + if( status == MQTTSuccess ) + { + status = validateSubscribeUnsubscribeParams( pContext, + pSubscriptionList, + subscriptionCount, + packetId, + MQTT_TYPE_UNSUBSCRIBE ); + } if( status == MQTTSuccess ) { /* Get the remaining length and packet size.*/ status = MQTT_GetUnsubscribePacketSize( pSubscriptionList, subscriptionCount, + pPropertyBuilder, &remainingLength, - &packetSize ); - LogDebug( ( "UNSUBSCRIBE packet size is %lu and remaining length is %lu.", - ( unsigned long ) packetSize, - ( unsigned long ) remainingLength ) ); + &packetSize, + pContext->connectProperties.serverMaxPacketSize ); + LogInfo( ( "UNSUBSCRIBE packet size is %lu and remaining length is %lu.", + ( unsigned long ) packetSize, + ( unsigned long ) remainingLength ) ); } if( status == MQTTSuccess ) @@ -3347,7 +3864,8 @@ MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, pSubscriptionList, subscriptionCount, packetId, - remainingLength ); + remainingLength, + pPropertyBuilder ); } MQTT_POST_STATE_UPDATE_HOOK( pContext ); @@ -3358,18 +3876,15 @@ MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext ) +MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext, + const MQTTPropBuilder_t * pPropertyBuilder, + MQTTSuccessFailReasonCode_t reasonCode ) { size_t packetSize = 0U; - int32_t sendResult = 0; + size_t remainingLength = 0U; MQTTStatus_t status = MQTTSuccess; - MQTTFixedBuffer_t localBuffer; - uint8_t disconnectPacket[ 2U ]; MQTTConnectionStatus_t connectStatus; - localBuffer.pBuffer = disconnectPacket; - localBuffer.size = 2U; - /* Validate arguments. */ if( pContext == NULL ) { @@ -3380,15 +3895,14 @@ MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext ) if( status == MQTTSuccess ) { /* Get MQTT DISCONNECT packet size. */ - status = MQTT_GetDisconnectPacketSize( &packetSize ); + status = MQTT_GetDisconnectPacketSize( pPropertyBuilder, &remainingLength, &packetSize, pContext->connectProperties.serverMaxPacketSize, reasonCode ); LogDebug( ( "MQTT DISCONNECT packet size is %lu.", ( unsigned long ) packetSize ) ); } if( status == MQTTSuccess ) { - /* Serialize MQTT DISCONNECT packet. */ - status = MQTT_SerializeDisconnect( &localBuffer ); + status = MQTT_ValidateDisconnectProperties( pContext->connectProperties.sessionExpiry, pPropertyBuilder ); } if( status == MQTTSuccess ) @@ -3414,23 +3928,7 @@ MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext ) LogError( ( "MQTT Connection Disconnected Successfully" ) ); - /* Here we do not use vectors as the disconnect packet has fixed fields - * which do not reside in user provided buffers. Thus, it can be sent - * using a simple send call. */ - sendResult = sendBuffer( pContext, - localBuffer.pBuffer, - packetSize ); - - if( sendResult < ( int32_t ) packetSize ) - { - LogError( ( "Transport send failed for DISCONNECT packet." ) ); - status = MQTTSendFailed; - } - else - { - LogDebug( ( "Sent %ld bytes of DISCONNECT packet.", - ( long int ) sendResult ) ); - } + status = sendDisconnectWithoutCopy( pContext, reasonCode, remainingLength, pPropertyBuilder ); } MQTT_POST_STATE_UPDATE_HOOK( pContext ); @@ -3768,3 +4266,569 @@ void MQTT_SerializeMQTTVec( uint8_t * pAllocatedMem, } /*-----------------------------------------------------------*/ + + +static MQTTStatus_t validatePublishAckReasonCode( MQTTSuccessFailReasonCode_t reasonCode ) +{ + MQTTStatus_t status = MQTTSuccess; + + switch( reasonCode ) + { + case MQTT_REASON_PUBACK_SUCCESS: + case MQTT_REASON_PUBACK_NO_MATCHING_SUBSCRIBERS: + case MQTT_REASON_PUBACK_UNSPECIFIED_ERROR: + case MQTT_REASON_PUBACK_IMPLEMENTATION_SPECIFIC_ERROR: + case MQTT_REASON_PUBACK_NOT_AUTHORIZED: + case MQTT_REASON_PUBACK_TOPIC_NAME_INVALID: + case MQTT_REASON_PUBACK_PACKET_IDENTIFIER_IN_USE: + case MQTT_REASON_PUBACK_QUOTA_EXCEEDED: + case MQTT_REASON_PUBACK_PAYLOAD_FORMAT_INVALID: + status = MQTTSuccess; + break; + + default: + status = MQTTBadParameter; + LogError( ( "Invalid Reason Code." ) ); + break; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t handleSuback( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ) +{ + MQTTStatus_t status = MQTTSuccess; + uint16_t packetIdentifier; + MQTTEventCallback_t appCallback; + MQTTDeserializedInfo_t deserializedInfo; + MQTTPropBuilder_t propBuffer = { 0 }; + + MQTTReasonCodeInfo_t ackInfo = { 0 }; + + assert( pContext != NULL ); + assert( pIncomingPacket != NULL ); + assert( pContext->appCallback != NULL ); + + appCallback = pContext->appCallback; + + status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL, &ackInfo, 0, pContext->connectProperties.maxPacketSize, &propBuffer, NULL ); + + LogInfo( ( "Ack packet deserialized with result: %s.", + MQTT_Status_strerror( status ) ) ); + + if( ( status == MQTTSuccess ) || ( status == MQTTServerRefused ) ) + { + deserializedInfo.packetIdentifier = packetIdentifier; + deserializedInfo.deserializationResult = status; + deserializedInfo.pPublishInfo = NULL; + deserializedInfo.pReasonCode = &ackInfo; + + /* Invoke application callback to hand the buffer over to application */ + appCallback( pContext, pIncomingPacket, &deserializedInfo, NULL, &pContext->ackPropsBuffer, &propBuffer ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t handleIncomingDisconnect( MQTTContext_t * pContext, + MQTTPacketInfo_t * pIncomingPacket ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTDeserializedInfo_t deserializedInfo = { 0 }; + MQTTPropBuilder_t propBuffer = { 0 }; + MQTTReasonCodeInfo_t reasonCode = { 0 }; + + + assert( pContext != NULL ); + assert( pContext->appCallback != NULL ); + assert( pIncomingPacket != NULL ); + + status = MQTT_DeserializeDisconnect( pIncomingPacket, pContext->connectProperties.maxPacketSize, &reasonCode, &propBuffer ); + + if( status == MQTTSuccess ) + { + deserializedInfo.pReasonCode = &reasonCode; + pContext->appCallback( pContext, pIncomingPacket, &deserializedInfo, NULL, &pContext->ackPropsBuffer, &propBuffer ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + + +static MQTTStatus_t initConnectProperties( MQTTConnectProperties_t * pConnectProperties ) +{ + MQTTStatus_t status = MQTTSuccess; + + assert( pConnectProperties != NULL ); + + pConnectProperties->receiveMax = UINT16_MAX; + pConnectProperties->maxPacketSize = MQTT_MAX_PACKET_SIZE; + pConnectProperties->requestProblemInfo = true; + pConnectProperties->serverReceiveMax = UINT16_MAX; + pConnectProperties->serverMaxQos = 1U; + pConnectProperties->serverMaxPacketSize = MQTT_MAX_PACKET_SIZE; + pConnectProperties->isWildcardAvailable = 1U; + pConnectProperties->isSubscriptionIdAvailable = 1U; + pConnectProperties->isSharedAvailable = 1U; + pConnectProperties->sessionExpiry = 0U; + pConnectProperties->topicAliasMax = 0U; + pConnectProperties->requestProblemInfo = true; + pConnectProperties->requestResponseInfo = false; + pConnectProperties->retainAvailable = 1U; + pConnectProperties->serverTopicAliasMax = 0U; + pConnectProperties->serverKeepAlive = UINT16_MAX; + + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_InitConnect( MQTTConnectProperties_t * pConnectProperties ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( pConnectProperties == NULL ) + { + LogError( ( "Invalid parameter: pConnectProperties is NULL." ) ); + status = MQTTBadParameter; + } + else + { + status = initConnectProperties( pConnectProperties ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t sendPublishAcksWithProperty( MQTTContext_t * pContext, + uint16_t packetId, + MQTTPublishState_t publishState, + MQTTSuccessFailReasonCode_t reasonCode ) +{ + int32_t bytesSentOrError; + size_t ioVectorLength = 0U; + size_t totalMessageLength = 0U; + MQTTStatus_t status = MQTTSuccess; + MQTTPublishState_t newState = MQTTStateNull; + uint8_t packetTypeByte = 0U; + MQTTPubAckType_t packetType; + size_t ackPropertyLength = 0U; + + /** + * Maximum number of bytes to send the Property Length. + * Property Length 0 + 4 = 4 + */ + uint8_t propertyLength[ 4U ]; + + /* Maximum number of bytes required by the fixed size properties and header. + * MQTT Control Byte 0 + 1 = 1 + * Remaining length (max) + 4 = 5 + * Packet Identifier + 2 = 7 + * Reason Code + 1 = 8 + */ + uint8_t pubAckHeader[ 8U ]; + size_t remainingLength = 0U; + size_t packetSize = 0U; + + /* The maximum vectors required to encode and send a publish ack. + * Ack Header 0 + 1 = 1 + * Property Length + 1 = 2 + * Properties + 1 = 3 + */ + + TransportOutVector_t pIoVector[ 3U ]; + + uint8_t * pIndex = pubAckHeader; + TransportOutVector_t * iterator = pIoVector; + + assert( pContext != NULL ); + + if( pContext->ackPropsBuffer.pBuffer != NULL ) + { + ackPropertyLength = pContext->ackPropsBuffer.currentIndex; + } + + packetTypeByte = getAckTypeToSend( publishState ); + + if( packetTypeByte != 0U ) + { + status = MQTT_ValidatePublishAckProperties( &pContext->ackPropsBuffer ); + } + + if( ( packetTypeByte != 0U ) && ( status == MQTTSuccess ) ) + { + status = validatePublishAckReasonCode( reasonCode ); + } + + if( ( packetTypeByte != 0U ) && ( status == MQTTSuccess ) ) + { + status = MQTT_GetAckPacketSize( &remainingLength, &packetSize, pContext->connectProperties.serverMaxPacketSize, ackPropertyLength ); + } + + if( pContext->connectStatus != MQTTConnected ) + { + status = ( pContext->connectStatus == MQTTNotConnected ) ? MQTTStatusNotConnected : MQTTStatusDisconnectPending; + } + + if( ( packetTypeByte != 0U ) && ( status == MQTTSuccess ) ) + { + packetType = getAckFromPacketType( packetTypeByte ); + /* Only for fixed size fields. */ + pIndex = MQTT_SerializeAckFixed( pIndex, + packetTypeByte, + packetId, + remainingLength, + reasonCode ); + iterator->iov_base = pubAckHeader; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - pubAckHeader ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + if( ( pContext->ackPropsBuffer.pBuffer != NULL ) && ( ackPropertyLength != 0U ) ) + { + /* Encode the property length. */ + pIndex = propertyLength; + pIndex = encodeVariableLength( propertyLength, ackPropertyLength ); + iterator->iov_base = propertyLength; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - propertyLength ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + /* Encode the properties. */ + iterator->iov_base = pContext->ackPropsBuffer.pBuffer; + iterator->iov_len = pContext->ackPropsBuffer.currentIndex; + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + /* + * Resetting buffer after sending the message. + */ + + pContext->ackPropsBuffer.currentIndex = 0; + pContext->ackPropsBuffer.fieldSet = 0; + } + + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + bytesSentOrError = sendMessageVector( pContext, pIoVector, ioVectorLength ); + + if( bytesSentOrError != ( int32_t ) totalMessageLength ) + { + LogError( ( "Failed to send ACK packet: PacketType=%02x, " + "PacketSize=%lu.", + ( unsigned int ) packetTypeByte, + packetSize ) ); + status = MQTTSendFailed; + } + + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + + if( status == MQTTSuccess ) + { + pContext->controlPacketSent = true; + + MQTT_PRE_STATE_UPDATE_HOOK( pContext ); + + status = MQTT_UpdateStateAck( pContext, + packetId, + packetType, + MQTT_SEND, + &newState ); + + MQTT_POST_STATE_UPDATE_HOOK( pContext ); + + if( status != MQTTSuccess ) + { + LogError( ( "Failed to update state of publish %hu.", + ( unsigned short ) packetId ) ); + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t sendDisconnectWithoutCopy( MQTTContext_t * pContext, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength, + const MQTTPropBuilder_t * pPropertyBuilder ) +{ + int32_t bytesSentOrError; + size_t ioVectorLength = 0U; + size_t totalMessageLength = 0U; + size_t disconnectPropLen = 0U; + MQTTStatus_t status = MQTTSuccess; + + /* Maximum number of bytes required by the fixed size part of the CONNECT + * packet header according to the MQTT specification. + * MQTT Control Byte 0 + 1 = 1 + * Remaining length (max) + 4 = 5 + * Reason Code + 1 = 6 + */ + uint8_t fixedHeader[ 6U ]; + + /** + * Maximum number of bytes to send the Property Length. + * Property Length 0 + 4 = 4 + */ + uint8_t propertyLength[ 4U ]; + + /* The maximum vectors required to encode and send a disconnect packet. The + * breakdown is shown below. + * Fixed header 0 + 1 = 1 + * Property Length + 1 = 2 + * Optional Properties + 1 = 3 + * */ + TransportOutVector_t pIoVector[ 3U ]; + + uint8_t * pIndex = fixedHeader; + TransportOutVector_t * iterator = pIoVector; + + assert( pContext != NULL ); + + /* Only for fixed size fields. */ + pIndex = MQTT_SerializeDisconnectFixed( pIndex, reasonCode, remainingLength ); + iterator->iov_base = fixedHeader; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - fixedHeader ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + disconnectPropLen = pPropertyBuilder->currentIndex; + } + + pIndex = propertyLength; + pIndex = encodeVariableLength( propertyLength, disconnectPropLen ); + iterator->iov_base = propertyLength; + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + iterator->iov_len = ( size_t ) ( pIndex - propertyLength ); + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + + if( disconnectPropLen > 0U ) + { + iterator->iov_base = pPropertyBuilder->pBuffer; + iterator->iov_len = pPropertyBuilder->currentIndex; + totalMessageLength += iterator->iov_len; + iterator++; + ioVectorLength++; + } + + bytesSentOrError = sendMessageVector( pContext, pIoVector, ioVectorLength ); + + if( bytesSentOrError != ( int32_t ) totalMessageLength ) + { + status = MQTTSendFailed; + LogError( ( "Failed to send disconnect packet." ) ); + } + + return status; +} + + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t validateSharedSubscriptions( const MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + const size_t iterator ) +{ + MQTTStatus_t status = MQTTSuccess; + bool isSharedSub = false; + const char * shareNameEnd; + const char * shareNameStart; + + isSharedSub = ( ( strncmp( pSubscriptionList[ iterator ].pTopicFilter, "$share/", 7 ) ) == 0 ); + + if( isSharedSub ) + { + shareNameStart = &( pSubscriptionList[ iterator ].pTopicFilter[ 7 ] ); + shareNameEnd = strchr( shareNameStart, ( int32_t ) '/' ); + + if( ( shareNameEnd == NULL ) || ( shareNameEnd == &( pSubscriptionList[ iterator ].pTopicFilter[ 7 ] ) ) ) + { + LogError( ( "Protocol Error : ShareName is not present , missing or empty" ) ); + status = MQTTBadParameter; + } + else if( pSubscriptionList[ iterator ].noLocalOption ) + { + LogError( ( "Protocol Error : noLocalOption cannot be 1 for shared subscriptions" ) ); + status = MQTTBadParameter; + } + else if( pContext->connectProperties.isSharedAvailable == 0U ) + { + LogError( ( "Protocol Error : Shared Subscriptions not allowed" ) ); + status = MQTTBadParameter; + } + else if( shareNameEnd[ 1 ] == '\0' ) + { + LogError( ( "Protocol Error : Topic filter after share name is missing" ) ); + status = MQTTBadParameter; + } + else + { + const char * ptr; + + for( ptr = shareNameStart; ptr < shareNameEnd; ptr++ ) + { + if( ( *ptr == '#' ) || ( *ptr == '+' ) ) + { + status = MQTTBadParameter; + break; /* Invalid share name */ + } + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static void addSubscriptionOptions( const MQTTSubscribeInfo_t pSubscriptionInfo, + uint8_t * subscriptionOptionsArray, + size_t currentOptionIndex ) +{ + uint8_t subscriptionOptions = 0U; + + if( pSubscriptionInfo.qos == MQTTQoS1 ) + { + LogInfo( ( "Adding QoS as QoS 1 in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_QOS1 ); + } + else if( pSubscriptionInfo.qos == MQTTQoS2 ) + { + LogInfo( ( "Adding QoS as QoS 2 in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_QOS2 ); + } + else + { + LogInfo( ( "Adding QoS as QoS 0 in SUBSCRIBE payload" ) ); + } + + if( pSubscriptionInfo.noLocalOption ) + { + LogInfo( ( "Adding noLocalOption in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_NO_LOCAL ); + } + else + { + LogDebug( ( "Adding noLocalOption as 0 in SUBSCRIBE payload" ) ); + } + + if( pSubscriptionInfo.retainAsPublishedOption ) + { + LogInfo( ( " retainAsPublishedOption in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_RETAIN_AS_PUBLISHED ); + } + else + { + LogDebug( ( "retainAsPublishedOption as 0 in SUBSCRIBE payload" ) ); + } + + if( pSubscriptionInfo.retainHandlingOption == retainSendOnSub ) + { + LogInfo( ( "Send Retain messages at the time of subscribe" ) ); + } + else if( pSubscriptionInfo.retainHandlingOption == retainSendOnSubIfNotPresent ) + { + LogInfo( ( "Send retained messages at subscribe only if the subscription does not currently exist" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_RETAIN_HANDLING1 ); + } + else + { + LogInfo( ( "Do not send retained messages at subscribe" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_RETAIN_HANDLING2 ); + } + + subscriptionOptionsArray[ currentOptionIndex ] = subscriptionOptions; +} + +/*-----------------------------------------------------------*/ + +static bool checkWildcardSubscriptions( uint8_t isWildcardAvailable, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t iterator ) +{ + bool ret = false; + + if( isWildcardAvailable == 0U ) + { + if( ( ( strchr( pSubscriptionList[ iterator ].pTopicFilter, ( int32_t ) '#' ) != NULL ) || ( strchr( pSubscriptionList[ iterator ].pTopicFilter, ( int32_t ) '+' ) != NULL ) ) ) + { + ret = true; + } + } + + return ret; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t validateSubscribeTopicFilter( const MQTTContext_t * pContext, + const MQTTSubscribeInfo_t * pSubscriptionList, + size_t iterator ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( ( pSubscriptionList[ iterator ].topicFilterLength == 0U ) || ( pSubscriptionList[ iterator ].pTopicFilter == NULL ) ) + { + LogError( ( "Argument cannot be null : pTopicFilter" ) ); + status = MQTTBadParameter; + } + else if( pSubscriptionList[ iterator ].qos > MQTTQoS2 ) + { + LogError( ( "Protocol Error : QoS cannot be greater than 2" ) ); + status = MQTTBadParameter; + } + else if( checkWildcardSubscriptions( pContext->connectProperties.isWildcardAvailable, pSubscriptionList, iterator ) ) + { + LogError( ( "Protocol Error : Wildcard Subscriptions not allowed. " ) ); + status = MQTTBadParameter; + } + else if( pSubscriptionList[ iterator ].retainHandlingOption > retainDoNotSendonSub ) + { + LogError( ( "Protocol Error : retainHandlingOption cannot be greater than 2" ) ); + status = MQTTBadParameter; + } + else + { + status = validateSharedSubscriptions( pContext, pSubscriptionList, iterator ); + } + + return status; +} + +/*-----------------------------------------------------------*/ diff --git a/source/core_mqtt_serializer.c b/source/core_mqtt_serializer.c index 729817629..9631a4f55 100644 --- a/source/core_mqtt_serializer.c +++ b/source/core_mqtt_serializer.c @@ -30,14 +30,7 @@ #include #include "core_mqtt_serializer.h" - -/* Include config defaults header to get default values of configs. */ -#include "core_mqtt_config_defaults.h" - -/** - * @brief MQTT protocol version 3.1.1. - */ -#define MQTT_VERSION_3_1_1 ( ( uint8_t ) 4U ) +#include "core_mqtt_utils.h" /** * @brief Size of the fixed and variable header of a CONNECT packet. @@ -63,24 +56,13 @@ #define MQTT_PUBLISH_FLAG_DUP ( 3 ) /**< @brief MQTT PUBLISH duplicate flag. */ /** - * @brief The size of MQTT DISCONNECT packets, per MQTT spec. - */ -#define MQTT_DISCONNECT_PACKET_SIZE ( 2UL ) - -/** - * @brief A PINGREQ packet is always 2 bytes in size, defined by MQTT 3.1.1 spec. + * @brief A PINGREQ packet is always 2 bytes in size, defined by MQTT 5.0 spec. */ #define MQTT_PACKET_PINGREQ_SIZE ( 2UL ) -/** - * @brief The Remaining Length field of MQTT disconnect packets, per MQTT spec. - */ -#define MQTT_DISCONNECT_REMAINING_LENGTH ( ( uint8_t ) 0 ) - /* - * Constants relating to CONNACK packets, defined by MQTT 3.1.1 spec. + * Constants relating to CONNACK packets, defined by MQTT spec. */ -#define MQTT_PACKET_CONNACK_REMAINING_LENGTH ( ( uint8_t ) 2U ) /**< @brief A CONNACK packet always has a "Remaining length" of 2. */ #define MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ( ( uint8_t ) 0x01U ) /**< @brief The "Session Present" bit is always the lowest bit. */ /* @@ -91,73 +73,130 @@ #define MQTT_PACKET_PINGRESP_REMAINING_LENGTH ( 0U ) /**< @brief A PINGRESP packet always has a "Remaining length" of 0. */ /** - * @brief Per the MQTT 3.1.1 spec, the largest "Remaining Length" of an MQTT + * @brief Per the MQTT spec, the largest "Remaining Length" of an MQTT * packet is this value, 256 MB. */ #define MQTT_MAX_REMAINING_LENGTH ( 268435455UL ) /** - * @brief Set a bit in an 8-bit unsigned integer. + * @brief Per the MQTT spec, the max packet size can be of max remaining length + 5 bytes */ -#define UINT8_SET_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) | ( 0x01U << ( position ) ) ) ) +#define MQTT_MAX_PACKET_SIZE ( 268435460U ) /** - * @brief Clear a bit in an 8-bit unsigned integer. + * @brief Version 5 has the value 5. */ -#define UINT8_CLEAR_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) & ( ~( 0x01U << ( position ) ) ) ) ) +#define MQTT_VERSION_5 ( 5U ) /** - * @brief Macro for checking if a bit is set in a 1-byte unsigned int. - * - * @param[in] x The unsigned int to check. - * @param[in] position Which bit to check. + * @ingroup mqtt_constants + * @brief The size of MQTT PUBACK, PUBREC, PUBREL, and PUBCOMP packets with reason code, packet id. */ -#define UINT8_CHECK_BIT( x, position ) ( ( ( x ) & ( 0x01U << ( position ) ) ) == ( 0x01U << ( position ) ) ) +#define MQTT_PUBLISH_ACK_PACKET_SIZE_WITH_REASON ( 3UL ) + +/* Position of the properties for the fieldSet*/ /** - * @brief Get the high byte of a 16-bit unsigned integer. + * @brief Position for Subscription Identifier property */ -#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( ( x ) >> 8 ) ) +#define MQTT_SUBSCRIPTION_ID_POS ( 1 ) /** - * @brief Get the low byte of a 16-bit unsigned integer. + * @brief Position for Session Expiry Interval property */ -#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( ( x ) & 0x00ffU ) ) +#define MQTT_SESSION_EXPIRY_INTERVAL_POS ( 2 ) /** - * @brief Macro for decoding a 2-byte unsigned int from a sequence of bytes. - * - * @param[in] ptr A uint8_t* that points to the high byte. + * @brief Position for Receive Maximum property */ -#define UINT16_DECODE( ptr ) \ - ( uint16_t ) ( ( ( ( uint16_t ) ptr[ 0 ] ) << 8 ) | \ - ( ( uint16_t ) ptr[ 1 ] ) ) +#define MQTT_RECEIVE_MAXIMUM_POS ( 3 ) /** - * @brief A value that represents an invalid remaining length. - * - * This value is greater than what is allowed by the MQTT specification. + * @brief Position for Maximum Packet Size property */ -#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 ) +#define MQTT_MAX_PACKET_SIZE_POS ( 4 ) /** - * @brief The minimum remaining length for a QoS 0 PUBLISH. - * - * Includes two bytes for topic name length and one byte for topic name. + * @brief Position for Topic Alias Maximum property */ -#define MQTT_MIN_PUBLISH_REMAINING_LENGTH_QOS0 ( 3U ) +#define MQTT_TOPIC_ALIAS_MAX_POS ( 5 ) -/*-----------------------------------------------------------*/ +/** + * @brief Position for Request Response Information property + */ +#define MQTT_REQUEST_RESPONSE_INFO_POS ( 6 ) +/** + * @brief Position for Request Problem Information property + */ +#define MQTT_REQUEST_PROBLEM_INFO_POS ( 7 ) /** - * @brief MQTT Subscription packet types. + * @brief Position for User Property */ -typedef enum MQTTSubscriptionType -{ - MQTT_SUBSCRIBE, /**< @brief The type is a SUBSCRIBE packet. */ - MQTT_UNSUBSCRIBE /**< @brief The type is a UNSUBSCRIBE packet. */ -} MQTTSubscriptionType_t; +#define MQTT_USER_PROPERTY_POS ( 8 ) + +/** + * @brief Position for Authentication Method property + */ +#define MQTT_AUTHENTICATION_METHOD_POS ( 9 ) + +/** + * @brief Position for Authentication Data property + */ +#define MQTT_AUTHENTICATION_DATA_POS ( 10 ) + +/** + * @brief Position for Payload Format Indicator property + */ +#define MQTT_PAYLOAD_FORMAT_INDICATOR_POS ( 11 ) + +/** + * @brief Position for Message Expiry Interval property + */ +#define MQTT_MESSAGE_EXPIRY_INTERVAL_POS ( 12 ) + +/** + * @brief Position for Topic Alias property in PUBLISH + */ +#define MQTT_PUBLISH_TOPIC_ALIAS_POS ( 13 ) + +/** + * @brief Position for Response Topic property in PUBLISH + */ +#define MQTT_PUBLISH_RESPONSE_TOPIC_POS ( 14 ) + +/** + * @brief Position for Correlation Data property in PUBLISH + */ +#define MQTT_PUBLISH_CORRELATION_DATA_POS ( 15 ) + +/** + * @brief Position for Subscription Identifier property in PUBLISH + */ +#define MQTT_PUBLISH_SUBSCRIPTION_IDENTIFIER_POS ( 16 ) + +/** + * @brief Position for Content Type property in PUBLISH + */ +#define MQTT_PUBLISH_CONTENT_TYPE_POS ( 17 ) + +/** + * @brief Position for Reason String property + */ +#define MQTT_REASON_STRING_POS ( 18 ) + +/** + * @brief Position for Will Delay Interval property + */ +#define MQTT_WILL_DELAY_POS ( 19 ) + +/** + * @brief A value that represents an invalid remaining length. + * + * This value is greater than what is allowed by the MQTT specification. + */ +#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 ) /*-----------------------------------------------------------*/ @@ -168,15 +207,19 @@ typedef enum MQTTSubscriptionType * Copy of the payload into the buffer is done as part of the serialization * only if @p serializePayload is true. * - * @brief param[in] pPublishInfo Publish information. - * @brief param[in] remainingLength Remaining length of the PUBLISH packet. - * @brief param[in] packetIdentifier Packet identifier of PUBLISH packet. - * @brief param[in, out] pFixedBuffer Buffer to which PUBLISH packet will be + * @param[in] pPublishInfo Publish information containing topic, QoS, payload and other + * PUBLISH packet fields. + * @param[in] pPublishProperties MQTT v5.0 properties for the PUBLISH packet. Can be NULL + * if no properties are needed. + * @param[in] remainingLength Remaining length of the PUBLISH packet. + * @param[in] packetIdentifier Packet identifier of PUBLISH packet. + * @param[in, out] pFixedBuffer Buffer to which PUBLISH packet will be * serialized. - * @brief param[in] serializePayload Copy payload to the serialized buffer + * @param[in] serializePayload Copy payload to the serialized buffer * only if true. Only PUBLISH header will be serialized if false. */ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo, + const MQTTPropBuilder_t * pPublishProperties, size_t remainingLength, uint16_t packetIdentifier, const MQTTFixedBuffer_t * pFixedBuffer, @@ -189,13 +232,18 @@ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo, * @param[in] pPublishInfo MQTT PUBLISH packet parameters. * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet. * @param[out] pPacketSize The total size of the MQTT PUBLISH packet. + * @param[in] maxPacketSize Max packet size allowed by the server. + * @param[in] publishPropertyLength Length of the optional properties in MQTT_PUBLISH + * * - * @return false if the packet would exceed the size allowed by the - * MQTT spec; true otherwise. + * @return MQTTBadParameter if the packet would exceed the size allowed by the + * MQTT spec; MQTTSuccess otherwise. */ -static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, - size_t * pRemainingLength, - size_t * pPacketSize ); +static MQTTStatus_t calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, + size_t * pRemainingLength, + size_t * pPacketSize, + uint32_t maxPacketSize, + size_t publishPropertyLength ); /** * @brief Calculates the packet size and remaining length of an MQTT @@ -205,17 +253,23 @@ static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, * @param[in] subscriptionCount The number of elements in pSubscriptionList. * @param[out] pRemainingLength The Remaining Length of the MQTT SUBSCRIBE or * UNSUBSCRIBE packet. - * @param[out] pPacketSize The total size of the MQTT MQTT SUBSCRIBE or - * UNSUBSCRIBE packet. - * @param[in] subscriptionType #MQTT_SUBSCRIBE or #MQTT_UNSUBSCRIBE. + * @param[out] pPacketSize The total size of the MQTT SUBSCRIBE or + * MQTT UNSUBSCRIBE packet. + * @param[in] subscribePropLen Length of the optional properties in MQTT_SUBSCRIBE or MQTT_UNSUBSCRIBE + * @param[in] maxPacketSize Maximum Packet Size allowed by the broker + * @param[in] subscriptionType #MQTT_TYPE_SUBSCRIBE or #MQTT_TYPE_UNSUBSCRIBE. * * #MQTTBadParameter if the packet would exceed the size allowed by the * MQTT spec or a subscription is empty; #MQTTSuccess otherwise. + * */ + static MQTTStatus_t calculateSubscriptionPacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, size_t * pRemainingLength, size_t * pPacketSize, + size_t subscribePropLen, + uint32_t maxPacketSize, MQTTSubscriptionType_t subscriptionType ); /** @@ -243,35 +297,18 @@ static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo * * @param[in] pConnectInfo MQTT CONNECT packet parameters. * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used. + * @param[in] pConnectProperties MQTT CONNECT properties. + * @param[in] pWillProperties MQTT Will properties. * @param[in] remainingLength Remaining Length of MQTT CONNECT packet. * @param[out] pFixedBuffer Buffer for packet serialization. */ static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, + const MQTTPropBuilder_t * pConnectProperties, + const MQTTPropBuilder_t * pWillProperties, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer ); -/** - * @brief Prints the appropriate message for the CONNACK response code if logs - * are enabled. - * - * @param[in] responseCode MQTT standard CONNACK response code. - */ -static void logConnackResponse( uint8_t responseCode ); - -/** - * @brief Encodes the remaining length of the packet using the variable length - * encoding scheme provided in the MQTT v3.1.1 specification. - * - * @param[out] pDestination The destination buffer to store the encoded remaining - * length. - * @param[in] length The remaining length to encode. - * - * @return The location of the byte following the encoded value. - */ -static uint8_t * encodeRemainingLength( uint8_t * pDestination, - size_t length ); - /** * @brief Retrieve the size of the remaining length if it were to be encoded. * @@ -279,7 +316,7 @@ static uint8_t * encodeRemainingLength( uint8_t * pDestination, * * @return The size of the remaining length if it were to be encoded. */ -static size_t remainingLengthEncodedSize( size_t length ); +static size_t variableLengthEncodedSize( size_t length ); /** * @brief Encode a string whose size is at maximum 16 bits in length. @@ -362,98 +399,392 @@ static MQTTStatus_t checkPublishRemainingLength( size_t remainingLength, static MQTTStatus_t processPublishFlags( uint8_t publishFlags, MQTTPublishInfo_t * pPublishInfo ); + +/** + * @brief Deserialize a PUBLISH packet received from the server. + * + * Converts the packet from a stream of bytes to an #MQTTPublishInfo_t and + * extracts the packet identifier. Also prints out debug log messages about the + * packet. + * + * @param[in] pIncomingPacket Pointer to an MQTT packet struct representing a + * PUBLISH. + * @param[out] pPacketId Packet identifier of the PUBLISH. + * @param[out] pPublishInfo Pointer to #MQTTPublishInfo_t where output is + * written. + * @param[out] propBuffer Pointer to the property buffer. + * @param[in] topicAliasMax Maximum allowed Topic Alias. + * + * @return #MQTTSuccess if PUBLISH is valid; #MQTTBadResponse + * if the PUBLISH packet doesn't follow MQTT spec. + */ +static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket, + uint16_t * pPacketId, + MQTTPublishInfo_t * pPublishInfo, + MQTTPropBuilder_t * propBuffer, + uint16_t topicAliasMax ); + /** - * @brief Deserialize a CONNACK packet. + * @brief Deserialize a PINGRESP packet. * * Converts the packet from a stream of bytes to an #MQTTStatus_t. * - * @param[in] pConnack Pointer to an MQTT packet struct representing a - * CONNACK. - * @param[out] pSessionPresent Whether a previous session was present. + * @param[in] pPingresp Pointer to an MQTT packet struct representing a PINGRESP. * - * @return #MQTTSuccess if CONNACK specifies that CONNECT was accepted; - * #MQTTServerRefused if CONNACK specifies that CONNECT was rejected; - * #MQTTBadResponse if the CONNACK packet doesn't follow MQTT spec. + * @return #MQTTSuccess if PINGRESP is valid; #MQTTBadResponse if the PINGRESP + * packet doesn't follow MQTT spec. */ -static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack, - bool * pSessionPresent ); +static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp ); + /** - * @brief Decode the status bytes of a SUBACK packet to a #MQTTStatus_t. + * @brief Validate the length and decode a user property. * - * @param[in] statusCount Number of status bytes in the SUBACK. - * @param[in] pStatusStart The first status byte in the SUBACK. + * @param[out] pPropertyLength Size of the length. + * @param[out] pIndex Pointer to the current index of the buffer. * - * @return #MQTTSuccess, #MQTTServerRefused, or #MQTTBadResponse. + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeAndDiscard( size_t * pPropertyLength, + uint8_t ** pIndex ); + +/** + * @brief Decodes the variable length by reading a single byte at a time. + * + * Uses the algorithm provided in the spec. + * + * @param[in] pBuffer Pointer to the buffer. + * @param[out] pLength Decoded variable length + * + * @return #MQTTSuccess if variable length and paramters are valid else #MQTTBadParameter. */ -static MQTTStatus_t readSubackStatus( size_t statusCount, - const uint8_t * pStatusStart ); +static MQTTStatus_t decodeVariableLength( const uint8_t * pBuffer, + size_t * pLength ); /** - * @brief Deserialize a SUBACK packet. + * @brief Validate the connack parameters. * * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts - * the packet identifier. + * the variable header without connack properties. + * + * @param[in] pIncomingPacket Pointer to an MQTT packet struct representing a incoming packet. + * @param[out] pSessionPresent Whether a session is present or not. * - * @param[in] pSuback Pointer to an MQTT packet struct representing a SUBACK. - * @param[out] pPacketIdentifier Packet ID of the SUBACK. * - * @return #MQTTSuccess if SUBACK is valid; #MQTTBadResponse if SUBACK packet - * doesn't follow the MQTT spec. + * @return #MQTTSuccess if connack without connack properties is valid; #MQTTBadResponse if the Connack + * packet doesn't follow MQTT spec. */ -static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * pSuback, - uint16_t * pPacketIdentifier ); +static MQTTStatus_t validateConnackParams( const MQTTPacketInfo_t * pIncomingPacket, + bool * pSessionPresent ); /** - * @brief Deserialize a PUBLISH packet received from the server. + * @brief Prints the appropriate message for the CONNACK response code if logs + * are enabled. + * + * @param[in] responseCode MQTT Verion 5 standard CONNACK response code. + * + * @return MQTTServerRefused if response code is valid and MQTTBadResponse if responseCode is invalid. + */ +static MQTTStatus_t logConnackResponse( uint8_t responseCode ); + +/** + * @brief Validate the length and decode a 4 byte value. + * + * @param[out] pProperty To store the decoded property. + * @param[out] pPropertyLength Size of the length. + * @param[out] pUsed Whether the property is decoded befire. + * @param[out] pIndex Pointer to the current index of the buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeuint32_t( uint32_t * pProperty, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ); + +/** + * @brief Validate the length and decode a 2 byte value. + * + * @param[out] pProperty To store the decoded property. + * @param[out] pPropertyLength Size of the length. + * @param[out] pUsed Whether the property is decoded befire. + * @param[out] pIndex Pointer to the current index of the buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse + **/ + +static MQTTStatus_t decodeuint16_t( uint16_t * pProperty, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ); + +/** + * @brief Validate the length and decode a 1 byte value. + * + * @param[out] pProperty To store the decoded property. + * @param[out] pPropertyLength Size of the length. + * @param[out] pUsed Whether the property is decoded befire. + * @param[out] pIndex Pointer to the current index of the buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeuint8_t( uint8_t * pProperty, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ); + +/** + * @brief Validate the length and decode a utf 8 string. + * + * @param[out] pProperty To store the decoded string. + * @param[out] pLength Size of the decoded utf-8 string. + * @param[out] pPropertyLength Size of the length. + * @param[out] pUsed Whether the property is decoded before. + * @param[out] pIndex Pointer to the current index of the buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeutf_8( const char ** pProperty, + uint16_t * pLength, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ); + +/** + * @brief Validate the length and decode the connack properties. + * + * @param[out] pConnackProperties To store the decoded property. + * @param[out] length Length of the property. + * @param[out] pIndex Pointer to the current index of the buffer. + * @param[out] propBuffer Pointer to the property buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t deserializeConnackProperties( MQTTConnectProperties_t * pConnackProperties, + size_t length, + uint8_t * pIndex, + MQTTPropBuilder_t * propBuffer ); + + +/** + * @brief Prints the appropriate message for the PUBREL, PUBACK response code if logs + * are enabled. + * + * @param[in] reasonCode MQTT Verion 5 standard PUBREL, PUBACK response code. + * @param[in] packetIdentifier Packet id of the ack packet. + * + * @return #MQTTSuccess, #MQTTServerRefused and #MQTTBadResponse. + */ +static MQTTStatus_t logAckResponse( uint8_t reasonCode, + uint16_t packetIdentifier ); + +/** + * @brief Prints the appropriate message for the CONNACK response code if logs + * are enabled. + * + * @param[in] reasonCode MQTT Verion 5 standard CONNACK response code. + * @param[in] packetIdentifier Packet id of the ack packet. + * + * @return #MQTTSuccess, #MQTTServerRefused and #MQTTBadResponse. + */ +static MQTTStatus_t logSimpleAckResponse( uint8_t reasonCode, + uint16_t packetIdentifier ); + +/** + * @brief Validate the length and decode the publish ack properties. + * + * @param[out] propBuffer To store the decoded property. + * @param[out] pIndex Pointer to the current index of the buffer. + * @param[out] remainingLength Remaining length of the incoming packet. + * + * + * @return #MQTTSuccess, #MQTTBadResponse and #MQTTBadResponse + **/ +static MQTTStatus_t decodeAckProperties( MQTTPropBuilder_t * propBuffer, + uint8_t * pIndex, + size_t remainingLength ); + +/** + * @brief Deserialize an PUBACK, PUBREC, PUBREL, or PUBCOMP packet. + * + * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts + * the packet identifier, reason code, properties. + * + * @param[in] pAck Pointer to the MQTT packet structure representing the packet. + * @param[out] pPacketIdentifier Packet ID of the ack type packet. + * @param[out] pReasonCode Structure to store reason code of the ack type packet. + * @param[in] requestProblem To validate the packet. + * @param[out] propBuffer Pointer to the property buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse, #MQTTBadResponse and #MQTTBadResponse. + */ +static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck, + uint16_t * pPacketIdentifier, + MQTTReasonCodeInfo_t * pReasonCode, + bool requestProblem, + MQTTPropBuilder_t * propBuffer ); + +/** + * @brief Prints and validates the appropriate message for the Disconnect response code if logs + * are enabled. + * + * @param[in] reasonCode MQTT Verion 5 standard DISCONNECT response code. + * @param[in] incoming To differentiate between outgoing and incoming disconnect. + * + * @return #MQTTSuccess,#MQTTBadParameter and #MQTTBadResponse. + */ +static MQTTStatus_t validateDisconnectResponse( uint8_t reasonCode, + bool incoming ); + +/** + * @brief Deserialize properties in the SUBACK packet received from the server. + * + * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts properties. + * + * @param[out] propBuffer Pointer to the property buffer. + * @param[in] pIndex Pointer to the start of the properties. + * @param[out] pSubackPropertyLength Pointer to the length of suback properties + * + * @return #MQTTSuccess if SUBACK is valid; #MQTTBadResponse + * + */ +static MQTTStatus_t deserializeSubackProperties( MQTTPropBuilder_t * propBuffer, + uint8_t * pIndex, + size_t * pSubackPropertyLength ); + +/** + * @brief Deserialize properties in the PUBLISH packet received from the server. * * Converts the packet from a stream of bytes to an #MQTTPublishInfo_t and - * extracts the packet identifier. Also prints out debug log messages about the - * packet. + * extracts properties. * - * @param[in] pIncomingPacket Pointer to an MQTT packet struct representing a - * PUBLISH. - * @param[out] pPacketId Packet identifier of the PUBLISH. * @param[out] pPublishInfo Pointer to #MQTTPublishInfo_t where output is * written. + * @param[in] propBuffer Pointer to the property buffer. + * @param[in] pIndex Pointer to the start of the properties. + * @param[in] topicAliasMax Maximum allowed Topic Alias. * * @return #MQTTSuccess if PUBLISH is valid; #MQTTBadResponse * if the PUBLISH packet doesn't follow MQTT spec. */ -static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket, - uint16_t * pPacketId, - MQTTPublishInfo_t * pPublishInfo ); +static MQTTStatus_t deserializePublishProperties( MQTTPublishInfo_t * pPublishInfo, + MQTTPropBuilder_t * propBuffer, + uint8_t * pIndex, + uint16_t topicAliasMax ); /** - * @brief Deserialize an UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP packet. + * @brief Validate the length and decode a utf_8 string * - * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts - * the packet identifier. + * @param[out] pPropertyLength Size of the length. + * @param[out] pUsed Whether the property is decoded before. + * @param[out] pIndex Pointer to the current index of the buffer. * - * @param[in] pAck Pointer to the MQTT packet structure representing the packet. - * @param[out] pPacketIdentifier Packet ID of the ack type packet. + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeAndDiscardutf_8( size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ); + +/** + * @brief Validate the length and decode a uint8_t + * + * @param[out] pPropertyLength Size of the length. + * @param[out] pUsed Whether the property is decoded before. + * @param[out] pIndex Pointer to the current index of the buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeAndDiscard_uint8( size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ); + +/** + * @brief Validate the length and decode a uint32_t + * + * @param[out] pPropertyLength Size of the length. + * @param[out] pUsed Whether the property is decoded before. + * @param[out] pIndex Pointer to the current index of the buffer. + * + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeAndDiscard_uint32( size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ); + +/** + * @brief Decode the status bytes of a SUBACK packet to a #MQTTStatus_t. * - * @return #MQTTSuccess if UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP is valid; - * #MQTTBadResponse if the packet doesn't follow the MQTT spec. + * @param[in] statusCount Number of status bytes in the SUBACK. + * @param[in] pStatusStart The first status byte in the SUBACK. + * @param[out] ackInfo The #MQTTReasonCodeInfo_t to store reason codes for each topic filter. + * @return #MQTTSuccess, #MQTTServerRefused, or #MQTTBadResponse. */ -static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck, - uint16_t * pPacketIdentifier ); +static MQTTStatus_t readSubackStatus( size_t statusCount, + const uint8_t * pStatusStart, + MQTTReasonCodeInfo_t * ackInfo ); /** - * @brief Deserialize a PINGRESP packet. + * @brief Validate the length and decode a utf 8 string. * - * Converts the packet from a stream of bytes to an #MQTTStatus_t. + * @param[out] pProperty To store the decoded string. + * @param[out] pLength Size of the decoded binary string. + * @param[out] pPropertyLength Size of the length. + * @param[out] pIndex Pointer to the current index of the buffer. * - * @param[in] pPingresp Pointer to an MQTT packet struct representing a PINGRESP. + * @return #MQTTSuccess, #MQTTBadResponse + **/ +static MQTTStatus_t decodeBinaryData( const void ** pProperty, + uint16_t * pLength, + size_t * pPropertyLength, + uint8_t ** pIndex ); + +/** + * @brief Encode binary data whose size is at maximum 16 bits in length. * - * @return #MQTTSuccess if PINGRESP is valid; #MQTTBadResponse if the PINGRESP - * packet doesn't follow MQTT spec. + * @param[out] pDestination Destination buffer for the encoding. + * @param[in] pSource The source binary data to encode. + * @param[in] sourceLength The length of the source data to encode. + * + * @return A pointer to the end of the encoded binary data. */ -static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp ); +static uint8_t * encodeBinaryData( uint8_t * pDestination, + const void * pSource, + uint16_t sourceLength ); + +/** + * @brief Deserialize an MQTT CONNACK packet. + * + * @param[out] pConnackProperties To store the deserialized connack properties. + * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer. + * @param[out] pSessionPresent Whether a previous session was present. + * @param[in] propBuffer MQTTPropBuilder_t to store the deserialized properties. + * + * @return #MQTTBadParameter, #MQTTBadResponse, #MQTTSuccess, #MQTTServerRefused + */ +MQTTStatus_t deserializeConnack( MQTTConnectProperties_t * pConnackProperties, + const MQTTPacketInfo_t * pIncomingPacket, + bool * pSessionPresent, + MQTTPropBuilder_t * propBuffer ); + +/** + * @brief Deserialize an MQTT SUBACK / UNSUBACK packet. + * + * @param[in] incomingPacket #MQTTPacketInfo_t containing the buffer. + * @param[in] pPacketId The packet ID obtained from the buffer. + * @param[in] subackReasonCodes Struct to store reason code(s) from the acknowledgment packet. + * Contains the success/failure status of the corresponding request. + * @param[in] propBuffer MQTTPropBuilder_t to store the deserialized properties. + * + * @return #MQTTBadParameter, #MQTTBadResponse, #MQTTSuccess, #MQTTServerRefused + */ +static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * incomingPacket, + uint16_t * pPacketId, + MQTTReasonCodeInfo_t * subackReasonCodes, + MQTTPropBuilder_t * propBuffer ); /*-----------------------------------------------------------*/ -static size_t remainingLengthEncodedSize( size_t length ) +static size_t variableLengthEncodedSize( size_t length ) { size_t encodedSize; @@ -490,39 +821,6 @@ static size_t remainingLengthEncodedSize( size_t length ) /*-----------------------------------------------------------*/ -static uint8_t * encodeRemainingLength( uint8_t * pDestination, - size_t length ) -{ - uint8_t lengthByte; - uint8_t * pLengthEnd = NULL; - size_t remainingLength = length; - - assert( pDestination != NULL ); - - pLengthEnd = pDestination; - - /* This algorithm is copied from the MQTT v3.1.1 spec. */ - do - { - lengthByte = ( uint8_t ) ( remainingLength % 128U ); - remainingLength = remainingLength / 128U; - - /* Set the high bit of this byte, indicating that there's more data. */ - if( remainingLength > 0U ) - { - UINT8_SET_BIT( lengthByte, 7 ); - } - - /* Output a single encoded byte. */ - *pLengthEnd = lengthByte; - pLengthEnd++; - } while( remainingLength > 0U ); - - return pLengthEnd; -} - -/*-----------------------------------------------------------*/ - static uint8_t * encodeString( uint8_t * pDestination, const char * pSource, uint16_t sourceLength ) @@ -555,11 +853,13 @@ static uint8_t * encodeString( uint8_t * pDestination, /*-----------------------------------------------------------*/ -static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, - size_t * pRemainingLength, - size_t * pPacketSize ) +static MQTTStatus_t calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, + size_t * pRemainingLength, + size_t * pPacketSize, + uint32_t maxPacketSize, + size_t publishPropertyLength ) { - bool status = true; + MQTTStatus_t status = MQTTSuccess; size_t packetSize = 0, payloadLimit = 0; assert( pPublishInfo != NULL ); @@ -578,6 +878,9 @@ static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, packetSize += sizeof( uint16_t ); } + packetSize += publishPropertyLength; + packetSize += variableLengthEncodedSize( publishPropertyLength ); + /* Calculate the maximum allowed size of the payload for the given parameters. * This calculation excludes the "Remaining length" encoding, whose size is not * yet known. */ @@ -592,9 +895,10 @@ static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, ( unsigned long ) pPublishInfo->payloadLength, ( unsigned long ) payloadLimit, MQTT_MAX_REMAINING_LENGTH ) ); - status = false; + status = MQTTBadParameter; } - else + + if( status == MQTTSuccess ) { /* Add the length of the PUBLISH payload. At this point, the "Remaining length" * has been calculated. */ @@ -602,7 +906,7 @@ static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, /* Now that the "Remaining length" is known, recalculate the payload limit * based on the size of its encoding. */ - payloadLimit -= remainingLengthEncodedSize( packetSize ); + payloadLimit -= variableLengthEncodedSize( packetSize ); /* Check that the given payload fits within the size allowed by MQTT spec. */ if( pPublishInfo->payloadLength > payloadLimit ) @@ -613,7 +917,7 @@ static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, ( unsigned long ) pPublishInfo->payloadLength, ( unsigned long ) payloadLimit, MQTT_MAX_REMAINING_LENGTH ) ); - status = false; + status = MQTTBadParameter; } else { @@ -621,7 +925,14 @@ static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, * size of the PUBLISH packet. */ *pRemainingLength = packetSize; - packetSize += 1U + remainingLengthEncodedSize( packetSize ); + packetSize += 1U + variableLengthEncodedSize( packetSize ); + + if( packetSize > maxPacketSize ) + { + LogError( ( "Packet size is greater than the allowed maximum packet size." ) ); + status = MQTTBadParameter; + } + *pPacketSize = packetSize; } } @@ -629,6 +940,7 @@ static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, LogDebug( ( "PUBLISH packet remaining length=%lu and packet size=%lu.", ( unsigned long ) *pRemainingLength, ( unsigned long ) *pPacketSize ) ); + return status; } @@ -652,7 +964,7 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * /* Length of serialized packet = First byte * + Length of encoded remaining length * + Encoded topic length. */ - headerLength = 1U + remainingLengthEncodedSize( remainingLength ) + 2U; + headerLength = 1U + variableLengthEncodedSize( remainingLength ) + 2U; if( pPublishInfo->qos == MQTTQoS1 ) { @@ -685,7 +997,7 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * pIndex++; /* The "Remaining length" is encoded from the second byte. */ - pIndex = encodeRemainingLength( pIndex, remainingLength ); + pIndex = encodeVariableLength( pIndex, remainingLength ); /* The first byte of a UTF-8 string is the high byte of the string length. */ *pIndex = UINT16_HIGH_BYTE( pPublishInfo->topicNameLength ); @@ -703,13 +1015,14 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * /*-----------------------------------------------------------*/ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo, + const MQTTPropBuilder_t * pPublishProperties, size_t remainingLength, uint16_t packetIdentifier, const MQTTFixedBuffer_t * pFixedBuffer, bool serializePayload ) { uint8_t * pIndex = NULL; - + size_t propertyLength = 0U; /* The first byte of a PUBLISH packet contains the packet type and flags. */ uint8_t publishFlags = MQTT_PACKET_TYPE_PUBLISH; @@ -724,6 +1037,11 @@ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo, /* Get the start address of the buffer. */ pIndex = pFixedBuffer->pBuffer; + if( ( pPublishProperties != NULL ) && ( pPublishProperties->pBuffer != NULL ) ) + { + propertyLength = pPublishProperties->currentIndex; + } + if( pPublishInfo->qos == MQTTQoS1 ) { LogDebug( ( "Adding QoS as QoS1 in PUBLISH flags." ) ); @@ -755,7 +1073,7 @@ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo, pIndex++; /* The "Remaining length" is encoded from the second byte. */ - pIndex = encodeRemainingLength( pIndex, remainingLength ); + pIndex = encodeVariableLength( pIndex, remainingLength ); /* The topic name is placed after the "Remaining length". */ pIndex = encodeString( pIndex, @@ -772,6 +1090,15 @@ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo, pIndex = &pIndex[ 2U ]; } + /* Properties are added after packet identifier. */ + pIndex = encodeVariableLength( pIndex, propertyLength ); + + if( propertyLength > 0U ) + { + ( void ) memcpy( ( void * ) pIndex, ( const void * ) pPublishProperties->pBuffer, propertyLength ); + pIndex = &pIndex[ propertyLength ]; + } + /* The payload is placed after the packet identifier. * Payload is copied over only if required by the flag serializePayload. * This will help reduce an unnecessary copy of the payload into the buffer. @@ -792,6 +1119,8 @@ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo, assert( ( ( size_t ) ( pIndex - pFixedBuffer->pBuffer ) ) <= pFixedBuffer->size ); } +/*-----------------------------------------------------------*/ + static size_t getRemainingLength( TransportRecv_t recvFunc, NetworkContext_t * pNetworkContext ) { @@ -831,7 +1160,7 @@ static size_t getRemainingLength( TransportRecv_t recvFunc, /* Check that the decoded remaining length conforms to the MQTT specification. */ if( remainingLength != MQTT_REMAINING_LENGTH_INVALID ) { - expectedSize = remainingLengthEncodedSize( remainingLength ); + expectedSize = variableLengthEncodedSize( remainingLength ); if( bytesDecoded != expectedSize ) { @@ -896,7 +1225,7 @@ static MQTTStatus_t processRemainingLength( const uint8_t * pBuffer, if( status == MQTTSuccess ) { /* Check that the decoded remaining length conforms to the MQTT specification. */ - expectedSize = remainingLengthEncodedSize( remainingLength ); + expectedSize = variableLengthEncodedSize( remainingLength ); if( bytesDecoded != expectedSize ) { @@ -931,6 +1260,7 @@ static bool incomingPacketValid( uint8_t packetType ) case MQTT_PACKET_TYPE_SUBACK: case MQTT_PACKET_TYPE_UNSUBACK: case MQTT_PACKET_TYPE_PINGRESP: + case MQTT_PACKET_TYPE_DISCONNECT: status = true; break; @@ -1046,114 +1376,158 @@ static MQTTStatus_t processPublishFlags( uint8_t publishFlags, /*-----------------------------------------------------------*/ -static void logConnackResponse( uint8_t responseCode ) +static MQTTStatus_t logConnackResponse( uint8_t responseCode ) { - const char * const pConnackResponses[ 6 ] = + MQTTStatus_t status = MQTTServerRefused; + + /* Log an error based on the CONNACK response code. */ + switch( responseCode ) { - "Connection accepted.", /* 0 */ - "Connection refused: unacceptable protocol version.", /* 1 */ - "Connection refused: identifier rejected.", /* 2 */ - "Connection refused: server unavailable", /* 3 */ - "Connection refused: bad user name or password.", /* 4 */ - "Connection refused: not authorized." /* 5 */ - }; + case ( uint8_t ) MQTT_REASON_CONNACK_UNSPECIFIED_ERROR: + LogError( ( "Connection refused: Unspecified error" ) ); + break; - /* Avoid unused parameter warning when assert and logs are disabled. */ - ( void ) responseCode; - ( void ) pConnackResponses; + case ( uint8_t ) MQTT_REASON_CONNACK_MALFORMED_PACKET: + LogError( ( "Connection refused: Malformed Packet." ) ); + break; - assert( responseCode <= 5U ); + case ( uint8_t ) MQTT_REASON_CONNACK_PROTOCOL_ERROR: + LogError( ( "Connection refused: Protocol Error." ) ); + break; - if( responseCode == 0u ) - { - /* Log at Debug level for a success CONNACK response. */ - LogDebug( ( "%s", pConnackResponses[ 0 ] ) ); - } - else - { - /* Log an error based on the CONNACK response code. */ - LogError( ( "%s", pConnackResponses[ responseCode ] ) ); + case ( uint8_t ) MQTT_REASON_CONNACK_IMPLEMENTATION_SPECIFIC_ERROR: + LogError( ( "Connection refused: Implementation specific error." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_UNSUPPORTED_PROTOCOL_VERSION: + LogError( ( "Connection refused: Unsupported Protocol Version." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_CLIENT_IDENTIFIER_NOT_VALID: + LogError( ( "Connection refused: Client Identifier not valid." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_BAD_USER_NAME_OR_PASSWORD: + LogError( ( "Connection refused: Bad User Name or Password." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_NOT_AUTHORIZED: + LogError( ( "Connection refused: Not authorized." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_SERVER_UNAVAILABLE: + LogError( ( "Connection refused: Server unavailable." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_SERVER_BUSY: + LogError( ( "Connection refused: Server busy." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_BANNED: + LogError( ( "Connection refused: Banned." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_BAD_AUTHENTICATION_METHOD: + LogError( ( "Connection refused: Bad authentication method." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_TOPIC_NAME_INVALID: + LogError( ( "Connection refused: Topic Name invalid." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_PACKET_TOO_LARGE: + LogError( ( "Connection refused: Packet too large ." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_QUOTA_EXCEEDED: + LogError( ( "Connection refused: Quota exceeded." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_PAYLOAD_FORMAT_INVALID: + LogError( ( "Connection refused: Payload format invalid." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_RETAIN_NOT_SUPPORTED: + LogError( ( "Connection refused: Retain not supported." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_QOS_NOT_SUPPORTED: + LogError( ( "Connection refused: QoS not supported." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_USE_ANOTHER_SERVER: + LogError( ( "Connection refused: Use another server." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_SERVER_MOVED: + LogError( ( "Connection refused: Server moved." ) ); + break; + + case ( uint8_t ) MQTT_REASON_CONNACK_CONNECTION_RATE_EXCEEDED: + LogError( ( "Connection refused: Connection rate exceeded." ) ); + break; + + default: + status = MQTTBadResponse; + break; } + + return status; } /*-----------------------------------------------------------*/ -static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack, - bool * pSessionPresent ) +MQTTStatus_t deserializeConnack( MQTTConnectProperties_t * pConnackProperties, + const MQTTPacketInfo_t * pIncomingPacket, + bool * pSessionPresent, + MQTTPropBuilder_t * propBuffer ) { MQTTStatus_t status = MQTTSuccess; - const uint8_t * pRemainingData = NULL; + size_t propertyLength = 0U; + size_t remainingLengthSize; + uint8_t * pVariableHeader = NULL; + MQTTStatus_t statusCopy = MQTTSuccess; - assert( pConnack != NULL ); - assert( pSessionPresent != NULL ); - pRemainingData = pConnack->pRemainingData; + /*Validate the arguments*/ + status = validateConnackParams( pIncomingPacket, pSessionPresent ); - /* According to MQTT 3.1.1, the second byte of CONNACK must specify a - * "Remaining length" of 2. */ - if( pConnack->remainingLength != MQTT_PACKET_CONNACK_REMAINING_LENGTH ) + if( status == MQTTServerRefused ) { - LogError( ( "CONNACK does not have remaining length of %u.", - ( unsigned int ) MQTT_PACKET_CONNACK_REMAINING_LENGTH ) ); - - status = MQTTBadResponse; + statusCopy = status; } - /* Check the reserved bits in CONNACK. The high 7 bits of the third byte - * in CONNACK must be 0. */ - else if( ( pRemainingData[ 0 ] | 0x01U ) != 0x01U ) + if( ( status == MQTTSuccess ) || ( status == MQTTServerRefused ) ) { - LogError( ( "Reserved bits in CONNACK incorrect." ) ); - - status = MQTTBadResponse; + pVariableHeader = pIncomingPacket->pRemainingData; + pVariableHeader = &pVariableHeader[ 2 ]; + remainingLengthSize = variableLengthEncodedSize( pIncomingPacket->remainingLength ); + status = decodeVariableLength( pVariableHeader, &propertyLength ); } - else + + /*Validate the packet size if max packet size is set*/ + if( ( status == MQTTSuccess ) ) { - /* Determine if the "Session Present" bit is set. This is the lowest bit of - * the third byte in CONNACK. */ - if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ) - == MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ) + if( ( pIncomingPacket->remainingLength + remainingLengthSize + 1U ) > ( pConnackProperties->maxPacketSize ) ) { - LogDebug( ( "CONNACK session present bit set." ) ); - *pSessionPresent = true; - - /* MQTT 3.1.1 specifies that the fourth byte in CONNACK must be 0 if the - * "Session Present" bit is set. */ - if( pRemainingData[ 1 ] != 0U ) - { - LogError( ( "Session Present bit is set, but connect return code in CONNACK is %u (nonzero).", - ( unsigned int ) pRemainingData[ 1 ] ) ); - status = MQTTBadResponse; - } + LogError( ( "CONNACK packet size greater than maxPacketSize allowed" ) ); + status = MQTTBadResponse; + } + /*Validate the remaining length*/ + else if( ( pIncomingPacket->remainingLength ) != ( 2U + propertyLength + variableLengthEncodedSize( propertyLength ) ) ) + { + LogError( ( "Invalid Remaining Length" ) ); + status = MQTTBadResponse; } + /*Deserialize the connack properties.*/ else { - LogDebug( ( "CONNACK session present bit not set." ) ); - *pSessionPresent = false; + status = deserializeConnackProperties( pConnackProperties, propertyLength, pVariableHeader, propBuffer ); } } if( status == MQTTSuccess ) { - /* In MQTT 3.1.1, only values 0 through 5 are valid CONNACK response codes. */ - if( pRemainingData[ 1 ] > 5U ) - { - LogError( ( "CONNACK response %u is invalid.", - ( unsigned int ) pRemainingData[ 1 ] ) ); - - status = MQTTBadResponse; - } - else - { - /* Print the appropriate message for the CONNACK response code if logs are - * enabled. */ - logConnackResponse( pRemainingData[ 1 ] ); - - /* A nonzero CONNACK response code means the connection was refused. */ - if( pRemainingData[ 1 ] > 0U ) - { - status = MQTTServerRefused; - } - } + status = statusCopy; } return status; @@ -1165,174 +1539,207 @@ static MQTTStatus_t calculateSubscriptionPacketSize( const MQTTSubscribeInfo_t * size_t subscriptionCount, size_t * pRemainingLength, size_t * pPacketSize, + size_t subscribePropLen, + uint32_t maxPacketSize, MQTTSubscriptionType_t subscriptionType ) { + size_t packetSize = 0U, i = 0U; MQTTStatus_t status = MQTTSuccess; - size_t i = 0, packetSize = 0; assert( pSubscriptionList != NULL ); assert( subscriptionCount != 0U ); - assert( pRemainingLength != NULL ); - assert( pPacketSize != NULL ); - /* The variable header of a subscription packet consists of a 2-byte packet - * identifier. */ + /*2 byte packet id*/ packetSize += sizeof( uint16_t ); - /* Sum the lengths of all subscription topic filters; add 1 byte for each - * subscription's QoS if type is MQTT_SUBSCRIBE. */ + + packetSize += subscribePropLen; + packetSize += variableLengthEncodedSize( subscribePropLen ); + for( i = 0; i < subscriptionCount; i++ ) { - /* Add the length of the topic filter. MQTT strings are prepended - * with 2 byte string length field. Hence 2 bytes are added to size. */ packetSize += pSubscriptionList[ i ].topicFilterLength + sizeof( uint16_t ); - /* Only SUBSCRIBE packets include the QoS. */ - if( subscriptionType == MQTT_SUBSCRIBE ) + if( subscriptionType == MQTT_TYPE_SUBSCRIBE ) { packetSize += 1U; } - - /* Validate each topic filter. */ - if( ( pSubscriptionList[ i ].topicFilterLength == 0U ) || - ( pSubscriptionList[ i ].pTopicFilter == NULL ) ) - { - status = MQTTBadParameter; - LogError( ( "Subscription #%lu in %sSUBSCRIBE packet cannot be empty.", - ( unsigned long ) i, - ( subscriptionType == MQTT_SUBSCRIBE ) ? "" : "UN" ) ); - /* It is not necessary to break as an error status has already been set. */ - } } /* At this point, the "Remaining length" has been calculated. Return error - * if the "Remaining length" exceeds what is allowed by MQTT 3.1.1. Otherwise, + * if the "Remaining length" exceeds what is allowed by MQTT 5. Otherwise, * set the output parameter.*/ if( packetSize > MQTT_MAX_REMAINING_LENGTH ) { - LogError( ( "Subscription packet length of %lu exceeds" - "the MQTT 3.1.1 maximum packet length of %lu.", + LogError( ( "Subscribe packet size %lu exceeds %d. " + "Packet size cannot be greater than %d.", ( unsigned long ) packetSize, - MQTT_MAX_REMAINING_LENGTH ) ); + UINT16_MAX, + UINT16_MAX ) ); status = MQTTBadParameter; } if( status == MQTTSuccess ) { *pRemainingLength = packetSize; - - /* Calculate the full size of the subscription packet by adding - * number of bytes required to encode the "Remaining length" field - * plus 1 byte for the "Packet type" field. */ - packetSize += 1U + remainingLengthEncodedSize( packetSize ); - - /*Set the pPacketSize output parameter. */ + packetSize += 1U + variableLengthEncodedSize( packetSize ); *pPacketSize = packetSize; } + if( packetSize > maxPacketSize ) + { + LogError( ( "Packet size is greater than the allowed maximum packet size." ) ); + status = MQTTBadParameter; + } + LogDebug( ( "Subscription packet remaining length=%lu and packet size=%lu.", ( unsigned long ) *pRemainingLength, ( unsigned long ) *pPacketSize ) ); - return status; } /*-----------------------------------------------------------*/ static MQTTStatus_t readSubackStatus( size_t statusCount, - const uint8_t * pStatusStart ) + const uint8_t * pStatusStart, + MQTTReasonCodeInfo_t * ackInfo ) { - MQTTStatus_t status = MQTTSuccess; + MQTTStatus_t status = MQTTServerRefused; uint8_t subscriptionStatus = 0; size_t i = 0; assert( pStatusStart != NULL ); - /* Iterate through each status byte in the SUBACK packet. */ for( i = 0; i < statusCount; i++ ) { - /* Read a single status byte in SUBACK. */ subscriptionStatus = pStatusStart[ i ]; - /* MQTT 3.1.1 defines the following values as status codes. */ switch( subscriptionStatus ) { case 0x00: case 0x01: case 0x02: - - LogDebug( ( "Topic filter %lu accepted, max QoS %u.", + LogDebug( ( "Topic Filter %lu accepted, max QoS %u.", ( unsigned long ) i, ( unsigned int ) subscriptionStatus ) ); + status = MQTTSuccess; break; case 0x80: + LogWarn( ( "Topic Filter Refused" ) ); + break; - LogWarn( ( "Topic filter %lu refused.", ( unsigned long ) i ) ); + case 0x83: + LogWarn( ( "Implementation specific error." ) ); + break; + + case 0x87: + LogWarn( ( "Not authorized." ) ); + break; + + case 0x8F: + LogWarn( ( "Topic Name Invalid." ) ); + break; + + case 0x91: + LogWarn( ( "Packet Identifier In Use." ) ); + break; + + case 0x97: + LogWarn( ( "Quota Exceeded." ) ); + break; + + case 0x9E: + LogWarn( ( "Shared Subscriptions Not Supported." ) ); + break; - /* Application should remove subscription from the list */ - status = MQTTServerRefused; + case 0xA1: + LogWarn( ( "Subscription Identifiers Not Supported." ) ); + break; + case 0xA2: + LogWarn( ( "Wildcard Subscriptions Not Supported." ) ); break; default: - LogError( ( "Bad SUBSCRIBE status %u.", + LogError( ( "Bad Subscribe status %u.", ( unsigned int ) subscriptionStatus ) ); - status = MQTTBadResponse; - break; } - /* Stop parsing the subscription statuses if a bad response was received. */ if( status == MQTTBadResponse ) { break; } } + if( ( status == MQTTSuccess ) || ( status == MQTTServerRefused ) ) + { + ackInfo->reasonCode = pStatusStart; + ackInfo->reasonCodeLength = statusCount; + } + return status; } /*-----------------------------------------------------------*/ -static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * pSuback, - uint16_t * pPacketIdentifier ) +static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * incomingPacket, + uint16_t * pPacketId, + MQTTReasonCodeInfo_t * subackReasonCodes, + MQTTPropBuilder_t * propBuffer ) { MQTTStatus_t status = MQTTSuccess; - size_t remainingLength; - const uint8_t * pVariableHeader = NULL; + uint8_t * pIndex = NULL; + size_t remainingLength = 0U; + size_t statusCount = 0U; + const uint8_t * pStatusStart; + size_t propertyLength = 0U; + + /* Validate input parameters using assert */ + assert( incomingPacket != NULL ); + assert( pPacketId != NULL ); + - assert( pSuback != NULL ); - assert( pPacketIdentifier != NULL ); + pIndex = incomingPacket->pRemainingData; + remainingLength = incomingPacket->remainingLength; - remainingLength = pSuback->remainingLength; - pVariableHeader = pSuback->pRemainingData; + pIndex = incomingPacket->pRemainingData; - /* A SUBACK must have a remaining length of at least 3 to accommodate the - * packet identifier and at least 1 return code. */ - if( remainingLength < 3U ) + if( subackReasonCodes == NULL ) + { + status = MQTTBadParameter; + } + else if( incomingPacket->remainingLength < 4U ) { - LogError( ( "SUBACK cannot have a remaining length less than 3." ) ); + LogError( ( "Suback Packet Cannot have a remaining Length of less than 4" ) ); status = MQTTBadResponse; } else { - /* Extract the packet identifier (first 2 bytes of variable header) from SUBACK. */ - *pPacketIdentifier = UINT16_DECODE( pVariableHeader ); - - LogDebug( ( "Packet identifier %hu.", - ( unsigned short ) *pPacketIdentifier ) ); + *pPacketId = UINT16_DECODE( pIndex ); + pIndex = &pIndex[ 2 ]; + LogDebug( ( "Packet Identifier is %hu.", + ( unsigned short ) *pPacketId ) ); - if( *pPacketIdentifier == 0U ) + if( *pPacketId == 0U ) { + LogError( ( "Packet Id cannot be 0" ) ); status = MQTTBadResponse; } - else - { - status = readSubackStatus( remainingLength - sizeof( uint16_t ), - &pVariableHeader[ sizeof( uint16_t ) ] ); - } + } + + if( ( status == MQTTSuccess ) && ( incomingPacket->remainingLength > 4U ) ) + { + status = deserializeSubackProperties( propBuffer, pIndex, &propertyLength ); + } + + if( status == MQTTSuccess ) + { + statusCount = remainingLength - sizeof( uint16_t ) - propertyLength - variableLengthEncodedSize( propertyLength ); + pStatusStart = &pIndex[ propertyLength + variableLengthEncodedSize( propertyLength ) ]; + status = readSubackStatus( statusCount, pStatusStart, subackReasonCodes ); } return status; @@ -1379,7 +1786,7 @@ static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo /* The serialized packet size = First byte * + length of encoded size of remaining length * + remaining length. */ - packetSize = 1U + remainingLengthEncodedSize( remainingLength ) + packetSize = 1U + variableLengthEncodedSize( remainingLength ) + remainingLength; if( packetSize > pFixedBuffer->size ) @@ -1397,121 +1804,71 @@ static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo /*-----------------------------------------------------------*/ -static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket, - uint16_t * pPacketId, - MQTTPublishInfo_t * pPublishInfo ) +MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket, + uint16_t * pPacketId, + MQTTPublishInfo_t * pPublishInfo, + MQTTPropBuilder_t * propBuffer, + uint32_t maxPacketSize, + uint16_t topicAliasMax ) { MQTTStatus_t status = MQTTSuccess; - const uint8_t * pVariableHeader, * pPacketIdentifierHigh = NULL; - - assert( pIncomingPacket != NULL ); - assert( pPacketId != NULL ); - assert( pPublishInfo != NULL ); - assert( pIncomingPacket->pRemainingData != NULL ); - pVariableHeader = pIncomingPacket->pRemainingData; - /* The flags are the lower 4 bits of the first byte in PUBLISH. */ - status = processPublishFlags( ( pIncomingPacket->type & 0x0FU ), pPublishInfo ); - - if( status == MQTTSuccess ) + if( ( pIncomingPacket == NULL ) || ( pPacketId == NULL ) || ( pPublishInfo == NULL ) ) { - /* Sanity checks for "Remaining length". A QoS 0 PUBLISH must have a remaining - * length of at least 3 to accommodate topic name length (2 bytes) and topic - * name (at least 1 byte). A QoS 1 or 2 PUBLISH must have a remaining length of - * at least 5 for the packet identifier in addition to the topic name length and - * topic name. */ - status = checkPublishRemainingLength( pIncomingPacket->remainingLength, - pPublishInfo->qos, - MQTT_MIN_PUBLISH_REMAINING_LENGTH_QOS0 ); + LogError( ( "Argument cannot be NULL: pIncomingPacket=%p, " + "pPacketId=%p, pPublishInfo=%p", + ( void * ) pIncomingPacket, + ( void * ) pPacketId, + ( void * ) pPublishInfo ) ); + status = MQTTBadParameter; } - - if( status == MQTTSuccess ) + else if( ( pIncomingPacket->type & 0xF0U ) != MQTT_PACKET_TYPE_PUBLISH ) { - /* Extract the topic name starting from the first byte of the variable header. - * The topic name string starts at byte 3 in the variable header. */ - pPublishInfo->topicNameLength = UINT16_DECODE( pVariableHeader ); - - /* Sanity checks for topic name length and "Remaining length". The remaining - * length must be at least as large as the variable length header. */ - status = checkPublishRemainingLength( pIncomingPacket->remainingLength, - pPublishInfo->qos, - pPublishInfo->topicNameLength + sizeof( uint16_t ) ); + LogError( ( "Packet is not publish. Packet type: %02x.", + ( unsigned int ) pIncomingPacket->type ) ); + status = MQTTBadParameter; } - - if( status == MQTTSuccess ) + else if( pIncomingPacket->pRemainingData == NULL ) { - /* Parse the topic. */ - pPublishInfo->pTopicName = ( const char * ) ( &pVariableHeader[ sizeof( uint16_t ) ] ); - LogDebug( ( "Topic name length: %hu.", ( unsigned short ) pPublishInfo->topicNameLength ) ); - - /* Extract the packet identifier for QoS 1 or 2 PUBLISH packets. Packet - * identifier starts immediately after the topic name. */ - /* coverity[tainted_scalar] */ - pPacketIdentifierHigh = ( const uint8_t * ) ( &pPublishInfo->pTopicName[ pPublishInfo->topicNameLength ] ); - - if( pPublishInfo->qos > MQTTQoS0 ) - { - *pPacketId = UINT16_DECODE( pPacketIdentifierHigh ); - - LogDebug( ( "Packet identifier %hu.", - ( unsigned short ) *pPacketId ) ); - - /* Advance pointer two bytes to start of payload as in the QoS 0 case. */ - pPacketIdentifierHigh = &pPacketIdentifierHigh[ sizeof( uint16_t ) ]; - - /* Packet identifier cannot be 0. */ - if( *pPacketId == 0U ) - { - LogError( ( "Packet identifier cannot be 0." ) ); - status = MQTTBadResponse; - } - } + LogError( ( "Argument cannot be NULL: " + "pIncomingPacket->pRemainingData is NULL." ) ); + status = MQTTBadParameter; } - - if( status == MQTTSuccess ) + else if( ( pIncomingPacket->remainingLength + variableLengthEncodedSize( pIncomingPacket->remainingLength ) + 1U ) > maxPacketSize ) { - /* Calculate the length of the payload. QoS 1 or 2 PUBLISH packets contain - * a packet identifier, but QoS 0 PUBLISH packets do not. */ - pPublishInfo->payloadLength = pIncomingPacket->remainingLength - pPublishInfo->topicNameLength - sizeof( uint16_t ); - - if( pPublishInfo->qos != MQTTQoS0 ) - { - /* Two more bytes for the packet identifier. */ - pPublishInfo->payloadLength -= sizeof( uint16_t ); - } - - /* Set payload if it exists. */ - pPublishInfo->pPayload = ( pPublishInfo->payloadLength != 0U ) ? pPacketIdentifierHigh : NULL; - - LogDebug( ( "Payload length %lu.", - ( unsigned long ) pPublishInfo->payloadLength ) ); + status = MQTTBadResponse; + } + else + { + status = deserializePublish( pIncomingPacket, pPacketId, pPublishInfo, propBuffer, topicAliasMax ); } return status; } - /*-----------------------------------------------------------*/ static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck, - uint16_t * pPacketIdentifier ) + uint16_t * pPacketIdentifier, + MQTTReasonCodeInfo_t * pReasonCode, + bool requestProblem, + MQTTPropBuilder_t * propBuffer ) { MQTTStatus_t status = MQTTSuccess; + uint8_t * pIndex = pAck->pRemainingData; - assert( pAck != NULL ); - assert( pPacketIdentifier != NULL ); - - /* Check that the "Remaining length" of the received ACK is 2. */ - if( pAck->remainingLength != MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH ) + if( pReasonCode == NULL ) + { + status = MQTTBadParameter; + } + else if( pAck->remainingLength < 2U ) { - LogError( ( "ACK does not have remaining length of %u.", - ( unsigned int ) MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH ) ); - status = MQTTBadResponse; } else { /* Extract the packet identifier (third and fourth bytes) from ACK. */ - *pPacketIdentifier = UINT16_DECODE( pAck->pRemainingData ); + *pPacketIdentifier = UINT16_DECODE( pIndex ); + pIndex = &pIndex[ 2 ]; LogDebug( ( "Packet identifier %hu.", ( unsigned short ) *pPacketIdentifier ) ); @@ -1524,6 +1881,27 @@ static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck, } } + /* If reason code is success, server can choose to not send the reason code.*/ + if( ( status == MQTTSuccess ) && ( pAck->remainingLength > 2U ) ) + { + pReasonCode->reasonCode = pIndex; + pReasonCode->reasonCodeLength = 1U; + pIndex++; + } + + if( ( pAck->remainingLength > 4U ) ) + { + /*Protocol error to send user property and reason string if client has set request problem to false.*/ + if( requestProblem == false ) + { + status = MQTTBadResponse; + } + else + { + status = decodeAckProperties( propBuffer, pIndex, pAck->remainingLength ); + } + } + return status; } @@ -1547,6 +1925,8 @@ static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp ) return status; } +/*-----------------------------------------------------------*/ + uint8_t * MQTT_SerializeConnectFixedHeader( uint8_t * pIndex, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, @@ -1559,17 +1939,20 @@ uint8_t * MQTT_SerializeConnectFixedHeader( uint8_t * pIndex, *pIndexLocal = MQTT_PACKET_TYPE_CONNECT; pIndexLocal++; - /* The remaining length of the CONNECT packet is encoded starting from the + /* The remaining length of the CONNECT packet is + * d starting from the * second byte. The remaining length does not include the length of the fixed * header or the encoding of the remaining length. */ - pIndexLocal = encodeRemainingLength( pIndexLocal, remainingLength ); + pIndexLocal = encodeVariableLength( pIndexLocal, remainingLength ); /* The string "MQTT" is placed at the beginning of the CONNECT packet's variable * header. This string is 4 bytes long. */ pIndexLocal = encodeString( pIndexLocal, "MQTT", 4 ); /* The MQTT protocol version is the second field of the variable header. */ - *pIndexLocal = MQTT_VERSION_3_1_1; + + *pIndexLocal = MQTT_VERSION_5; + pIndexLocal++; /* Set the clean session flag if needed. */ @@ -1624,14 +2007,19 @@ uint8_t * MQTT_SerializeConnectFixedHeader( uint8_t * pIndex, return pIndexLocal; } + /*-----------------------------------------------------------*/ static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, + const MQTTPropBuilder_t * pConnectProperties, + const MQTTPropBuilder_t * pWillProperties, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer ) { uint8_t * pIndex = NULL; + size_t connectPropertyLength = 0U; + size_t willPropertyLength = 0U; assert( pConnectInfo != NULL ); assert( pFixedBuffer != NULL ); @@ -1645,6 +2033,25 @@ static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo, pWillInfo, remainingLength ); + if( ( pConnectProperties != NULL ) && ( pConnectProperties->pBuffer != NULL ) ) + { + connectPropertyLength = pConnectProperties->currentIndex; + } + + if( ( pWillProperties != NULL ) && ( pWillProperties->pBuffer != NULL ) ) + { + willPropertyLength = pWillProperties->currentIndex; + } + + /* Write the properties length into the CONNECT packet. */ + pIndex = encodeVariableLength( pIndex, connectPropertyLength ); + + if( connectPropertyLength > 0U ) + { + ( void ) memcpy( ( void * ) pIndex, ( const void * ) pConnectProperties->pBuffer, connectPropertyLength ); + pIndex = &pIndex[ connectPropertyLength ]; + } + /* Write the client identifier into the CONNECT packet. */ pIndex = encodeString( pIndex, pConnectInfo->pClientIdentifier, @@ -1653,6 +2060,14 @@ static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo, /* Write the will topic name and message into the CONNECT packet if provided. */ if( pWillInfo != NULL ) { + pIndex = encodeVariableLength( pIndex, willPropertyLength ); + + if( willPropertyLength > 0U ) + { + ( void ) memcpy( ( void * ) pIndex, ( const void * ) pWillProperties->pBuffer, willPropertyLength ); + pIndex = &pIndex[ willPropertyLength ]; + } + pIndex = encodeString( pIndex, pWillInfo->pTopicName, pWillInfo->topicNameLength ); @@ -1686,13 +2101,17 @@ static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo, MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, + const MQTTPropBuilder_t * pConnectProperties, + const MQTTPropBuilder_t * pWillProperties, size_t * pRemainingLength, size_t * pPacketSize ) { MQTTStatus_t status = MQTTSuccess; size_t remainingLength; + size_t propertyLength = 0U; + size_t willPropertyLength = 0U; - /* The CONNECT packet will always include a 10-byte variable header. */ + /* The CONNECT packet will always include a 10-byte variable header without connect properties. */ size_t connectPacketSize = MQTT_PACKET_CONNECT_HEADER_SIZE; /* Validate arguments. */ @@ -1715,9 +2134,7 @@ MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, { /* The MQTTPublishInfo_t is reused for the will message. The payload * length for any other message could be larger than 65,535, but - * the will message length is required to be represented in 2 bytes. - * By bounding the payloadLength of the will message, the CONNECT - * packet will never be larger than 327699 bytes. */ + * the will message length is required to be represented in 2 bytes.*/ LogError( ( "The Will Message length must not exceed %d. " "pWillInfo->payloadLength=%lu.", UINT16_MAX, @@ -1726,12 +2143,36 @@ MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, } else { + /*Do Nothing*/ + } + + if( ( pConnectProperties != NULL ) && ( pConnectProperties->pBuffer != NULL ) ) + { + propertyLength = pConnectProperties->currentIndex; + } + + if( ( pWillProperties != NULL ) && ( pWillProperties->pBuffer != NULL ) ) + { + willPropertyLength = pWillProperties->currentIndex; + } + + if( status == MQTTSuccess ) + { + connectPacketSize += propertyLength; + connectPacketSize += variableLengthEncodedSize( propertyLength ); /* Add the length of the client identifier. */ connectPacketSize += pConnectInfo->clientIdentifierLength + sizeof( uint16_t ); + /* Add the lengths of the will properties if provided. */ + } + + if( status == MQTTSuccess ) + { /* Add the lengths of the will message and topic name if provided. */ if( pWillInfo != NULL ) { + connectPacketSize += willPropertyLength; + connectPacketSize += variableLengthEncodedSize( willPropertyLength ); connectPacketSize += pWillInfo->topicNameLength + sizeof( uint16_t ) + pWillInfo->payloadLength + sizeof( uint16_t ); } @@ -1753,24 +2194,21 @@ MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, /* Calculate the full size of the MQTT CONNECT packet by adding the size of * the "Remaining Length" field plus 1 byte for the "Packet Type" field. */ - connectPacketSize += 1U + remainingLengthEncodedSize( connectPacketSize ); - - /* The connectPacketSize calculated from this function's parameters is - * guaranteed to be less than the maximum MQTT CONNECT packet size, which - * is 327700. If the maximum client identifier length, the maximum will - * message topic length, the maximum will topic payload length, the - * maximum username length, and the maximum password length are all present - * in the MQTT CONNECT packet, the total size will be calculated to be - * 327699: - * (variable length header)10 + - * (maximum client identifier length) 65535 + (encoded length) 2 + - * (maximum will message topic name length) 65535 + (encoded length)2 + - * (maximum will message payload length) 65535 + 2 + - * (maximum username length) 65535 + (encoded length) 2 + - * (maximum password length) 65535 + (encoded length) 2 + - * (packet type field length) 1 + - * (CONNECT packet encoded length) 3 = 327699 */ + connectPacketSize += 1U + variableLengthEncodedSize( connectPacketSize ); + + /* + * It is possible that the remaining length becomes more than the maximum allowed by the MQTTV5-Spec, + * i.e. 268,435,455. This is because the user may enter a large number of user properties for the connect packet and/or the last will. + * Hence we need to have a check for this case + */ + if( remainingLength > MQTT_MAX_REMAINING_LENGTH ) + { + status = MQTTBadParameter; + } + } + if( status == MQTTSuccess ) + { *pRemainingLength = remainingLength; *pPacketSize = connectPacketSize; @@ -1786,6 +2224,8 @@ MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, + const MQTTPropBuilder_t * pConnectProperties, + const MQTTPropBuilder_t * pWillProperties, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer ) { @@ -1817,7 +2257,7 @@ MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, /* Calculate CONNECT packet size. Overflow in in this addition is not checked * because it is part of the API contract to call Mqtt_GetConnectPacketSize() * before this function. */ - connectPacketSize = remainingLength + remainingLengthEncodedSize( remainingLength ) + 1U; + connectPacketSize = remainingLength + variableLengthEncodedSize( remainingLength ) + 1U; /* Check that the full packet size fits within the given buffer. */ if( connectPacketSize > pFixedBuffer->size ) @@ -1832,6 +2272,8 @@ MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, { serializeConnectPacket( pConnectInfo, pWillInfo, + pConnectProperties, + pWillProperties, remainingLength, pFixedBuffer ); } @@ -1844,35 +2286,37 @@ MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, + const MQTTPropBuilder_t * pSubscribeProperties, size_t * pRemainingLength, - size_t * pPacketSize ) + size_t * pPacketSize, + uint32_t maxPacketSize ) { MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; - /* Validate parameters. */ - if( ( pSubscriptionList == NULL ) || ( pRemainingLength == NULL ) || - ( pPacketSize == NULL ) ) + if( ( pSubscribeProperties != NULL ) && ( pSubscribeProperties->pBuffer != NULL ) ) { - LogError( ( "Argument cannot be NULL: pSubscriptionList=%p, " - "pRemainingLength=%p, pPacketSize=%p.", - ( void * ) pSubscriptionList, - ( void * ) pRemainingLength, - ( void * ) pPacketSize ) ); + propertyLength = pSubscribeProperties->currentIndex; + } + + if( pSubscriptionList == NULL ) + { + LogError( ( "Argument cannot be null : SubscriptionList" ) ); status = MQTTBadParameter; } else if( subscriptionCount == 0U ) { - LogError( ( "subscriptionCount is 0." ) ); + LogError( ( "Subscription count cannot be 0" ) ); + status = MQTTBadParameter; + } + else if( maxPacketSize == 0U ) + { + LogError( ( "Max Packet size cannot be 0" ) ); status = MQTTBadParameter; } else { - /* Calculate the MQTT SUBSCRIBE packet size. */ - status = calculateSubscriptionPacketSize( pSubscriptionList, - subscriptionCount, - pRemainingLength, - pPacketSize, - MQTT_SUBSCRIBE ); + status = calculateSubscriptionPacketSize( pSubscriptionList, subscriptionCount, pRemainingLength, pPacketSize, propertyLength, maxPacketSize, MQTT_TYPE_SUBSCRIBE ); } return status; @@ -1891,7 +2335,7 @@ uint8_t * MQTT_SerializeSubscribeHeader( size_t remainingLength, pIterator++; /* Encode the "Remaining length" starting from the second byte. */ - pIterator = encodeRemainingLength( pIterator, remainingLength ); + pIterator = encodeVariableLength( pIterator, remainingLength ); /* Place the packet identifier into the SUBSCRIBE packet. */ pIterator[ 0 ] = UINT16_HIGH_BYTE( packetId ); @@ -1915,7 +2359,7 @@ uint8_t * MQTT_SerializeUnsubscribeHeader( size_t remainingLength, pIterator++; /* Encode the "Remaining length" starting from the second byte. */ - pIterator = encodeRemainingLength( pIterator, remainingLength ); + pIterator = encodeVariableLength( pIterator, remainingLength ); /* Place the packet identifier into the SUBSCRIBE packet. */ pIterator[ 0 ] = UINT16_HIGH_BYTE( packetId ); @@ -1926,17 +2370,27 @@ uint8_t * MQTT_SerializeUnsubscribeHeader( size_t remainingLength, return pIterator; } +/*-----------------------------------------------------------*/ + MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, + const MQTTPropBuilder_t * pSubscribeProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer ) { size_t i = 0; uint8_t * pIndex = NULL; + size_t propertyLength = 0U; + MQTTStatus_t status; + + if( ( pSubscribeProperties != NULL ) && ( pSubscribeProperties->pBuffer != NULL ) ) + { + propertyLength = pSubscribeProperties->currentIndex; + } /* Validate all the parameters. */ - MQTTStatus_t status = + status = validateSubscriptionSerializeParams( pSubscriptionList, subscriptionCount, packetId, @@ -1950,17 +2404,76 @@ MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionL pIndex = MQTT_SerializeSubscribeHeader( remainingLength, pIndex, packetId ); + /*Serialize properties*/ + pIndex = encodeVariableLength( pIndex, propertyLength ); + + if( propertyLength > 0U ) + { + ( void ) memcpy( ( void * ) pIndex, ( const void * ) pSubscribeProperties->pBuffer, propertyLength ); + pIndex = &pIndex[ propertyLength ]; + } /* Serialize each subscription topic filter and QoS. */ for( i = 0; i < subscriptionCount; i++ ) { + uint8_t subscriptionOptions = 0U; pIndex = encodeString( pIndex, pSubscriptionList[ i ].pTopicFilter, pSubscriptionList[ i ].topicFilterLength ); - /* Place the QoS in the SUBSCRIBE packet. */ - *pIndex = ( uint8_t ) ( pSubscriptionList[ i ].qos ); - pIndex++; + /* Place the subscription options */ + if( pSubscriptionList[ i ].qos == MQTTQoS1 ) + { + LogInfo( ( "Adding QoS as QoS 1 in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_QOS1 ); + } + else if( pSubscriptionList[ i ].qos == MQTTQoS2 ) + { + LogInfo( ( "Adding QoS as QoS 2 in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_QOS2 ); + } + else + { + LogInfo( ( "Adding QoS as QoS 0 in SUBSCRIBE payload" ) ); + } + + if( pSubscriptionList[ i ].noLocalOption ) + { + LogInfo( ( "Adding noLocalOption in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_NO_LOCAL ); + } + else + { + LogDebug( ( "Adding noLocalOption as 0 in SUBSCRIBE payload" ) ); + } + + if( pSubscriptionList[ i ].retainAsPublishedOption ) + { + LogInfo( ( " retainAsPublishedOption in SUBSCRIBE payload" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_RETAIN_AS_PUBLISHED ); + } + else + { + LogDebug( ( "retainAsPublishedOption as 0 in SUBSCRIBE payload" ) ); + } + + if( pSubscriptionList[ i ].retainHandlingOption == retainSendOnSub ) + { + LogInfo( ( "Send Retain messages at the time of subscribe" ) ); + } + else if( pSubscriptionList[ i ].retainHandlingOption == retainSendOnSubIfNotPresent ) + { + LogInfo( ( "Send retained messages at subscribe only if the subscription does not currently exist" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_RETAIN_HANDLING1 ); + } + else + { + LogInfo( ( "Do not send retained messages at subscribe" ) ); + UINT8_SET_BIT( subscriptionOptions, MQTT_SUBSCRIBE_RETAIN_HANDLING2 ); + } + + *pIndex = subscriptionOptions; + pIndex = &pIndex[ 1 ]; } LogDebug( ( "Length of serialized SUBSCRIBE packet is %lu.", @@ -1974,10 +2487,18 @@ MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionL MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, + const MQTTPropBuilder_t * pUnsubscribeProperties, size_t * pRemainingLength, - size_t * pPacketSize ) + size_t * pPacketSize, + uint32_t maxPacketSize ) { MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + + if( ( pUnsubscribeProperties != NULL ) && ( pUnsubscribeProperties->pBuffer != NULL ) ) + { + propertyLength = pUnsubscribeProperties->currentIndex; + } /* Validate parameters. */ if( ( pSubscriptionList == NULL ) || ( pRemainingLength == NULL ) || @@ -2002,7 +2523,9 @@ MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscri subscriptionCount, pRemainingLength, pPacketSize, - MQTT_UNSUBSCRIBE ); + propertyLength, + maxPacketSize, + MQTT_TYPE_UNSUBSCRIBE ); } return status; @@ -2012,14 +2535,21 @@ MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscri MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, + const MQTTPropBuilder_t * pUnsubscribeProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer ) { MQTTStatus_t status = MQTTSuccess; size_t i = 0; + size_t propertyLength = 0; uint8_t * pIndex = NULL; + if( ( pUnsubscribeProperties != NULL ) && ( pUnsubscribeProperties->pBuffer != NULL ) ) + { + propertyLength = pUnsubscribeProperties->currentIndex; + } + /* Validate all the parameters. */ status = validateSubscriptionSerializeParams( pSubscriptionList, subscriptionCount, @@ -2034,6 +2564,16 @@ MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptio pIndex = MQTT_SerializeUnsubscribeHeader( remainingLength, pIndex, packetId ); + /*Serialize the properties */ + + pIndex = encodeVariableLength( pIndex, propertyLength ); + + if( propertyLength > 0U ) + { + ( void ) memcpy( ( void * ) pIndex, ( const void * ) pUnsubscribeProperties->pBuffer, propertyLength ); + pIndex = &pIndex[ propertyLength ]; + } + /* Serialize each subscription topic filter. */ for( i = 0; i < subscriptionCount; i++ ) { @@ -2052,10 +2592,18 @@ MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptio /*-----------------------------------------------------------*/ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, + const MQTTPropBuilder_t * pPublishProperties, size_t * pRemainingLength, - size_t * pPacketSize ) + size_t * pPacketSize, + uint32_t maxPacketSize ) { MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + + if( ( pPublishProperties != NULL ) && ( pPublishProperties->pBuffer != NULL ) ) + { + propertyLength = pPublishProperties->currentIndex; + } if( ( pPublishInfo == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) ) { @@ -2066,25 +2614,9 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, ( void * ) pPacketSize ) ); status = MQTTBadParameter; } - else if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) ) - { - LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, " - "topicNameLength=%hu.", - ( void * ) pPublishInfo->pTopicName, - ( unsigned short ) pPublishInfo->topicNameLength ) ); - status = MQTTBadParameter; - } else { - /* Calculate the "Remaining length" field and total packet size. If it exceeds - * what is allowed in the MQTT standard, return an error. */ - if( calculatePublishPacketSize( pPublishInfo, pRemainingLength, pPacketSize ) == false ) - { - LogError( ( "PUBLISH packet remaining length exceeds %lu, which is the " - "maximum size allowed by MQTT 3.1.1.", - MQTT_MAX_REMAINING_LENGTH ) ); - status = MQTTBadParameter; - } + status = calculatePublishPacketSize( pPublishInfo, pRemainingLength, pPacketSize, maxPacketSize, propertyLength ); } return status; @@ -2093,6 +2625,7 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, /*-----------------------------------------------------------*/ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, + const MQTTPropBuilder_t * pPublishProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer ) @@ -2149,7 +2682,7 @@ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, /* Length of serialized packet = First byte * + Length of encoded remaining length * + Remaining length. */ - packetSize = 1U + remainingLengthEncodedSize( remainingLength ) + packetSize = 1U + variableLengthEncodedSize( remainingLength ) + remainingLength; } @@ -2166,6 +2699,7 @@ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, { /* Serialize publish with header and payload. */ serializePublishCommon( pPublishInfo, + pPublishProperties, remainingLength, packetId, pFixedBuffer, @@ -2178,6 +2712,7 @@ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, /*-----------------------------------------------------------*/ MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo, + const MQTTPropBuilder_t * pPublishProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer, @@ -2228,7 +2763,7 @@ MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo * + Remaining length * - Payload Length. */ - packetSize = 1U + remainingLengthEncodedSize( remainingLength ) + packetSize = 1U + variableLengthEncodedSize( remainingLength ) + remainingLength - pPublishInfo->payloadLength; } @@ -2246,6 +2781,7 @@ MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo { /* Serialize publish without copying the payload. */ serializePublishCommon( pPublishInfo, + pPublishProperties, remainingLength, packetId, pFixedBuffer, @@ -2315,19 +2851,76 @@ MQTTStatus_t MQTT_SerializeAck( const MQTTFixedBuffer_t * pFixedBuffer, /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize ) +MQTTStatus_t MQTT_GetDisconnectPacketSize( const MQTTPropBuilder_t * pDisconnectProperties, + size_t * pRemainingLength, + size_t * pPacketSize, + uint32_t maxPacketSize, + MQTTSuccessFailReasonCode_t reasonCode ) { MQTTStatus_t status = MQTTSuccess; + size_t length = 0U; + size_t packetSize = 0U; + size_t propertyLength = 0U; - if( pPacketSize == NULL ) + if( ( pDisconnectProperties != NULL ) && ( pDisconnectProperties->pBuffer != NULL ) ) { - LogError( ( "pPacketSize is NULL." ) ); + propertyLength = pDisconnectProperties->currentIndex; + } + + /*Validate the arguments.*/ + if( ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) ) + { + LogError( ( "Argument cannot be NULL:" + "pRemainingLength=%p, pPacketSize=%p.", + ( void * ) pRemainingLength, + ( void * ) pPacketSize ) ); + status = MQTTBadParameter; + } + else if( maxPacketSize == 0U ) + { + LogError( ( "Max packet size cannot be zero." ) ); + status = MQTTBadParameter; + } + else if( validateDisconnectResponse( ( uint8_t ) reasonCode, false ) != MQTTSuccess ) + { + LogError( ( "Invalid reason code." ) ); status = MQTTBadParameter; } else { - /* MQTT DISCONNECT packets always have the same size. */ - *pPacketSize = MQTT_DISCONNECT_PACKET_SIZE; + /*Reason code.*/ + length += 1U; + } + + if( status == MQTTSuccess ) + { + /*Validate the length.*/ + if( ( propertyLength + variableLengthEncodedSize( propertyLength ) + 1U ) < MQTT_MAX_REMAINING_LENGTH ) + { + /*We have successfully calculated the property length.*/ + length += variableLengthEncodedSize( propertyLength ) + propertyLength; + *pRemainingLength = length; + } + else + { + status = MQTTBadParameter; + } + } + + if( status == MQTTSuccess ) + { + /*Packet size should be less than max allowed by the server.*/ + packetSize = length + 1U + variableLengthEncodedSize( length ); + + if( packetSize > maxPacketSize ) + { + status = MQTTBadParameter; + LogError( ( "Packet Size greater than Max Packet Size specified in the CONNACK" ) ); + } + else + { + *pPacketSize = packetSize; + } } return status; @@ -2335,9 +2928,20 @@ MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize ) /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer ) +MQTTStatus_t MQTT_SerializeDisconnect( const MQTTPropBuilder_t * pDisconnectProperties, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength, + const MQTTFixedBuffer_t * pFixedBuffer ) { MQTTStatus_t status = MQTTSuccess; + uint8_t * pIndex = NULL; + size_t packetSize = 0; + size_t propertyLength = 0; + + if( ( pDisconnectProperties != NULL ) && ( pDisconnectProperties->pBuffer != NULL ) ) + { + propertyLength = pDisconnectProperties->currentIndex; + } /* Validate arguments. */ if( pFixedBuffer == NULL ) @@ -2352,25 +2956,38 @@ MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer ) } else { - /* Empty else MISRA 15.7 */ + /* Length of serialized packet = First byte + * + Length of encoded remaining length + * + Remaining length. */ + packetSize = 1U + variableLengthEncodedSize( remainingLength ) + remainingLength; } if( status == MQTTSuccess ) { - if( pFixedBuffer->size < MQTT_DISCONNECT_PACKET_SIZE ) + if( pFixedBuffer->size < packetSize ) { LogError( ( "Buffer size of %lu is not sufficient to hold " "serialized DISCONNECT packet of size of %lu.", ( unsigned long ) pFixedBuffer->size, - MQTT_DISCONNECT_PACKET_SIZE ) ); + ( unsigned long ) packetSize ) ); status = MQTTNoMemory; } } if( status == MQTTSuccess ) { - pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT; - pFixedBuffer->pBuffer[ 1 ] = MQTT_DISCONNECT_REMAINING_LENGTH; + pIndex = pFixedBuffer->pBuffer; + pIndex = MQTT_SerializeDisconnectFixed( pIndex, + reasonCode, + remainingLength ); + + pIndex = encodeVariableLength( pIndex, propertyLength ); + + if( propertyLength > 0U ) + { + ( void ) memcpy( ( void * ) pIndex, ( const void * ) pDisconnectProperties->pBuffer, propertyLength ); + pIndex = &pIndex[ propertyLength ]; + } } return status; @@ -2441,36 +3058,64 @@ MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer ) /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket, - uint16_t * pPacketId, - MQTTPublishInfo_t * pPublishInfo ) +MQTTStatus_t MQTT_GetIncomingPacketTypeAndLength( TransportRecv_t readFunc, + NetworkContext_t * pNetworkContext, + MQTTPacketInfo_t * pIncomingPacket ) { MQTTStatus_t status = MQTTSuccess; + int32_t bytesReceived = 0; - if( ( pIncomingPacket == NULL ) || ( pPacketId == NULL ) || ( pPublishInfo == NULL ) ) + if( pIncomingPacket == NULL ) { - LogError( ( "Argument cannot be NULL: pIncomingPacket=%p, " - "pPacketId=%p, pPublishInfo=%p", - ( void * ) pIncomingPacket, - ( void * ) pPacketId, - ( void * ) pPublishInfo ) ); + LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) ); status = MQTTBadParameter; } - else if( ( pIncomingPacket->type & 0xF0U ) != MQTT_PACKET_TYPE_PUBLISH ) + else { - LogError( ( "Packet is not publish. Packet type: %02x.", - ( unsigned int ) pIncomingPacket->type ) ); - status = MQTTBadParameter; + /* Read a single byte. */ + bytesReceived = readFunc( pNetworkContext, + &( pIncomingPacket->type ), + 1U ); } - else if( pIncomingPacket->pRemainingData == NULL ) + + if( bytesReceived == 1 ) { - LogError( ( "Argument cannot be NULL: " - "pIncomingPacket->pRemainingData is NULL." ) ); - status = MQTTBadParameter; + /* Check validity. */ + if( incomingPacketValid( pIncomingPacket->type ) == true ) + { + pIncomingPacket->remainingLength = getRemainingLength( readFunc, + pNetworkContext ); + + if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID ) + { + LogError( ( "Incoming packet remaining length invalid." ) ); + status = MQTTBadResponse; + } + } + else + { + LogError( ( "Incoming packet invalid: Packet type=%u.", + ( unsigned int ) pIncomingPacket->type ) ); + status = MQTTBadResponse; + } + } + else if( ( status != MQTTBadParameter ) && ( bytesReceived == 0 ) ) + { + status = MQTTNoDataAvailable; + } + + /* If the input packet was valid, then any other number of bytes received is + * a failure. */ + else if( status != MQTTBadParameter ) + { + LogError( ( "A single byte was not read from the transport: " + "transportStatus=%ld.", + ( long int ) bytesReceived ) ); + status = MQTTRecvFailed; } else { - status = deserializePublish( pIncomingPacket, pPacketId, pPublishInfo ); + /* Empty else MISRA 15.7 */ } return status; @@ -2478,75 +3123,54 @@ MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket, /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket, - uint16_t * pPacketId, - bool * pSessionPresent ) -{ - MQTTStatus_t status = MQTTSuccess; +MQTTStatus_t MQTT_ProcessIncomingPacketTypeAndLength( const uint8_t * pBuffer, + const size_t * pIndex, + MQTTPacketInfo_t * pIncomingPacket ) +{ + MQTTStatus_t status = MQTTSuccess; if( pIncomingPacket == NULL ) { - LogError( ( "pIncomingPacket cannot be NULL." ) ); + LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) ); status = MQTTBadParameter; } - - /* Pointer for packet identifier cannot be NULL for packets other than - * CONNACK and PINGRESP. */ - else if( ( pPacketId == NULL ) && - ( ( pIncomingPacket->type != MQTT_PACKET_TYPE_CONNACK ) && - ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) ) + else if( pIndex == NULL ) { - LogError( ( "pPacketId cannot be NULL for packet type %02x.", - ( unsigned int ) pIncomingPacket->type ) ); + LogError( ( "Invalid parameter: pIndex is NULL." ) ); status = MQTTBadParameter; } - /* Pointer for session present cannot be NULL for CONNACK. */ - else if( ( pSessionPresent == NULL ) && - ( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) ) + else if( pBuffer == NULL ) { - LogError( ( "pSessionPresent cannot be NULL for CONNACK packet." ) ); + LogError( ( "Invalid parameter: pBuffer is NULL." ) ); status = MQTTBadParameter; } - - /* Pointer for remaining data cannot be NULL for packets other - * than PINGRESP. */ - else if( ( pIncomingPacket->pRemainingData == NULL ) && - ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) + /* There should be at least one byte in the buffer */ + else if( *pIndex < 1U ) { - LogError( ( "Remaining data of incoming packet is NULL." ) ); - status = MQTTBadParameter; + /* No data is available. There are 0 bytes received from the network + * receive function. */ + status = MQTTNoDataAvailable; } else { - /* Make sure response packet is a valid ack. */ - switch( pIncomingPacket->type ) - { - case MQTT_PACKET_TYPE_CONNACK: - status = deserializeConnack( pIncomingPacket, pSessionPresent ); - break; - - case MQTT_PACKET_TYPE_SUBACK: - status = deserializeSuback( pIncomingPacket, pPacketId ); - break; - - case MQTT_PACKET_TYPE_PINGRESP: - status = deserializePingresp( pIncomingPacket ); - break; - - case MQTT_PACKET_TYPE_UNSUBACK: - case MQTT_PACKET_TYPE_PUBACK: - case MQTT_PACKET_TYPE_PUBREC: - case MQTT_PACKET_TYPE_PUBREL: - case MQTT_PACKET_TYPE_PUBCOMP: - status = deserializeSimpleAck( pIncomingPacket, pPacketId ); - break; + /* At least one byte is present which should be deciphered. */ + pIncomingPacket->type = pBuffer[ 0 ]; + } - /* Any other packet type is invalid. */ - default: - LogError( ( "IotMqtt_DeserializeResponse() called with unknown packet type:(%02x).", - ( unsigned int ) pIncomingPacket->type ) ); - status = MQTTBadResponse; - break; + if( status == MQTTSuccess ) + { + /* Check validity. */ + if( incomingPacketValid( pIncomingPacket->type ) == true ) + { + status = processRemainingLength( pBuffer, + pIndex, + pIncomingPacket ); + } + else + { + LogError( ( "Incoming packet invalid: Packet type=%u.", + ( unsigned int ) pIncomingPacket->type ) ); + status = MQTTBadResponse; } } @@ -2555,149 +3179,3898 @@ MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket, /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_GetIncomingPacketTypeAndLength( TransportRecv_t readFunc, - NetworkContext_t * pNetworkContext, - MQTTPacketInfo_t * pIncomingPacket ) + +static MQTTStatus_t decodeAndDiscard( size_t * pPropertyLength, + uint8_t ** pIndex ) { + uint8_t * pVariableHeader = *pIndex; MQTTStatus_t status = MQTTSuccess; - int32_t bytesReceived = 0; + uint16_t keyLength; + uint16_t valueLength; + const char * pKey; + const char * pValue; - if( pIncomingPacket == NULL ) + /*Validate the property length and decode the user property received.*/ + if( *pPropertyLength < sizeof( uint16_t ) ) { - LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) ); - status = MQTTBadParameter; + status = MQTTBadResponse; } else { - /* Read a single byte. */ - bytesReceived = readFunc( pNetworkContext, - &( pIncomingPacket->type ), - 1U ); - } + keyLength = UINT16_DECODE( pVariableHeader ); + *pPropertyLength -= sizeof( uint16_t ); - if( bytesReceived == 1 ) - { - /* Check validity. */ - if( incomingPacketValid( pIncomingPacket->type ) == true ) + if( *pPropertyLength < keyLength ) { - pIncomingPacket->remainingLength = getRemainingLength( readFunc, - pNetworkContext ); + status = MQTTBadResponse; + } + else + { + pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ]; + pKey = ( const char * ) pVariableHeader; + *pPropertyLength -= keyLength; + pVariableHeader = &pVariableHeader[ keyLength ]; - if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID ) + if( *pPropertyLength < sizeof( uint16_t ) ) { - LogError( ( "Incoming packet remaining length invalid." ) ); status = MQTTBadResponse; } - } - else - { - LogError( ( "Incoming packet invalid: Packet type=%u.", - ( unsigned int ) pIncomingPacket->type ) ); - status = MQTTBadResponse; + else + { + valueLength = UINT16_DECODE( pVariableHeader ); + *pPropertyLength -= sizeof( uint16_t ); + pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ]; + + if( *pPropertyLength < ( size_t ) ( valueLength ) ) + { + status = MQTTBadResponse; + } + else + { + pValue = ( const char * ) pVariableHeader; + pVariableHeader = &pVariableHeader[ valueLength ]; + *pPropertyLength -= valueLength; + } + } } } - else if( ( status != MQTTBadParameter ) && ( bytesReceived == 0 ) ) + + *pIndex = pVariableHeader; + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeAndDiscardutf_8( size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ) +{ + uint8_t * pVariableHeader = *pIndex; + MQTTStatus_t status = MQTTSuccess; + uint16_t propertyLength; + + /*Protocol error to include the same property twice.*/ + + if( *pUsed == true ) { - status = MQTTNoDataAvailable; + status = MQTTBadResponse; } - - /* If the input packet was valid, then any other number of bytes received is - * a failure. */ - else if( status != MQTTBadParameter ) + else if( *pPropertyLength < sizeof( uint16_t ) ) { - LogError( ( "A single byte was not read from the transport: " - "transportStatus=%ld.", - ( long int ) bytesReceived ) ); - status = MQTTRecvFailed; + status = MQTTBadResponse; } else { - /* Empty else MISRA 15.7 */ + propertyLength = UINT16_DECODE( pVariableHeader ); + pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ]; + *pPropertyLength -= sizeof( uint16_t ); + + if( *pPropertyLength < propertyLength ) + { + status = MQTTBadResponse; + } + else + { + pVariableHeader = &pVariableHeader[ propertyLength ]; + *pPropertyLength -= propertyLength; + *pUsed = true; + } } + *pIndex = pVariableHeader; return status; } /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_UpdateDuplicatePublishFlag( uint8_t * pHeader, - bool set ) +static MQTTStatus_t decodeAndDiscard_uint8( size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ) { MQTTStatus_t status = MQTTSuccess; + uint8_t * pVariableHeader = *pIndex; - if( pHeader == NULL ) - { - LogError( ( "Header cannot be NULL" ) ); - status = MQTTBadParameter; - } - else if( ( ( *pHeader ) & 0xF0U ) != MQTT_PACKET_TYPE_PUBLISH ) + if( *pPropertyLength < sizeof( uint8_t ) ) { - LogError( ( "Header is not publish packet header" ) ); - status = MQTTBadParameter; + status = MQTTBadResponse; } - else if( set == true ) + else if( *pUsed == true ) { - UINT8_SET_BIT( *pHeader, MQTT_PUBLISH_FLAG_DUP ); + status = MQTTBadResponse; } else { - UINT8_CLEAR_BIT( *pHeader, MQTT_PUBLISH_FLAG_DUP ); + /* Skip 1 byte */ + pVariableHeader = &pVariableHeader[ sizeof( uint8_t ) ]; + *pPropertyLength -= sizeof( uint8_t ); + *pUsed = true; } + *pIndex = pVariableHeader; return status; } /*-----------------------------------------------------------*/ -MQTTStatus_t MQTT_ProcessIncomingPacketTypeAndLength( const uint8_t * pBuffer, - const size_t * pIndex, - MQTTPacketInfo_t * pIncomingPacket ) +static MQTTStatus_t decodeAndDiscard_uint32( size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ) { MQTTStatus_t status = MQTTSuccess; + uint8_t * pVariableHeader = *pIndex; - if( pIncomingPacket == NULL ) + if( *pPropertyLength < sizeof( uint32_t ) ) { - LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) ); - status = MQTTBadParameter; + status = MQTTBadResponse; } - else if( pIndex == NULL ) + else if( *pUsed == true ) { - LogError( ( "Invalid parameter: pIndex is NULL." ) ); - status = MQTTBadParameter; + status = MQTTBadResponse; } - else if( pBuffer == NULL ) + else { - LogError( ( "Invalid parameter: pBuffer is NULL." ) ); - status = MQTTBadParameter; + /* Skip 4 bytes */ + pVariableHeader = &pVariableHeader[ sizeof( uint32_t ) ]; + *pPropertyLength -= sizeof( uint32_t ); + *pUsed = true; } - /* There should be at least one byte in the buffer */ - else if( *pIndex < 1U ) + + *pIndex = pVariableHeader; + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t validateConnackParams( const MQTTPacketInfo_t * pIncomingPacket, + bool * pSessionPresent ) +{ + MQTTStatus_t status = MQTTSuccess; + const uint8_t * pRemainingData = NULL; + + /*Validate the arguments via asserts. */ + assert( pIncomingPacket != NULL ); + assert( pSessionPresent != NULL ); + assert( pIncomingPacket->pRemainingData != NULL ); + assert( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ); + + pRemainingData = pIncomingPacket->pRemainingData; + + if( ( pRemainingData[ 0 ] | 0x01U ) != 0x01U ) { - /* No data is available. There are 0 bytes received from the network - * receive function. */ - status = MQTTNoDataAvailable; + LogError( ( "Reserved bits in CONNACK incorrect." ) ); + + status = MQTTBadResponse; } else { - /* At least one byte is present which should be deciphered. */ - pIncomingPacket->type = pBuffer[ 0 ]; + /* Determine if the "Session Present" bit is set. This is the lowest bit of + * the third byte in CONNACK. */ + if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ) == MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ) + { + LogDebug( ( "CONNACK session present bit set." ) ); + *pSessionPresent = true; + + /* MQTT 5 specifies that the fourth byte in CONNACK must be 0 if the + * "Session Present" bit is set. */ + if( pRemainingData[ 1 ] != 0U ) + { + LogError( ( "Session Present bit is set, but connect return code in CONNACK is %u (nonzero).", + ( unsigned int ) pRemainingData[ 1 ] ) ); + status = MQTTBadResponse; + } + } + else + { + LogDebug( ( "CONNACK session present bit not set." ) ); + *pSessionPresent = false; + } } if( status == MQTTSuccess ) { - /* Check validity. */ - if( incomingPacketValid( pIncomingPacket->type ) == true ) + if( pRemainingData[ 1 ] != 0U ) { - status = processRemainingLength( pBuffer, - pIndex, - pIncomingPacket ); + status = logConnackResponse( pRemainingData[ 1 ] ); } - else + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeVariableLength( const uint8_t * pBuffer, + size_t * pLength ) +{ + size_t remainingLength = 0; + size_t multiplier = 1; + size_t bytesDecoded = 0; + size_t expectedSize = 0; + uint8_t encodedByte = 0; + MQTTStatus_t status = MQTTSuccess; + + /* This algorithm is copied from the MQTT v3.1.1 spec. */ + do + { + if( multiplier > 2097152U ) /* 128 ^ 3 */ { - LogError( ( "Incoming packet invalid: Packet type=%u.", - ( unsigned int ) pIncomingPacket->type ) ); + remainingLength = MQTT_REMAINING_LENGTH_INVALID; + + LogError( ( "Invalid remaining length in the packet.\n" ) ); + status = MQTTBadResponse; } + else + { + /* Get the next byte. It is at the next position after the bytes + * decoded till now since the header of one byte was read before. */ + encodedByte = pBuffer[ bytesDecoded ]; + remainingLength += ( ( size_t ) encodedByte & 0x7FU ) * multiplier; + multiplier *= 128U; + bytesDecoded++; + } + + /* If the response is incorrect then + * break out of the loop. */ + if( remainingLength == MQTT_REMAINING_LENGTH_INVALID ) + { + break; + } + } while( ( encodedByte & 0x80U ) != 0U ); + + if( status == MQTTSuccess ) + { + /* Check that the decoded remaining length conforms to the MQTT specification. */ + expectedSize = variableLengthEncodedSize( remainingLength ); + + if( bytesDecoded != expectedSize ) + { + LogError( ( "Expected and actual length of decoded bytes do not match.\n" ) ); + status = MQTTBadResponse; + } + else + { + *pLength = remainingLength; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeuint32_t( uint32_t * pProperty, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ) +{ + uint8_t * pVariableHeader = *pIndex; + MQTTStatus_t status = MQTTSuccess; + + /*Protocol error to include the same property twice.*/ + if( *pUsed == true ) + { + status = MQTTBadResponse; + } + /*Validate the length and decode.*/ + else if( *pPropertyLength < sizeof( uint32_t ) ) + { + status = MQTTBadResponse; + } + else + { + *pProperty = UINT32_DECODE( pVariableHeader ); + pVariableHeader = &pVariableHeader[ sizeof( uint32_t ) ]; + *pUsed = true; + *pPropertyLength -= sizeof( uint32_t ); + } + + *pIndex = pVariableHeader; + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeuint16_t( uint16_t * pProperty, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ) +{ + uint8_t * pVariableHeader = *pIndex; + MQTTStatus_t status = MQTTSuccess; + + /*Protocol error to include the same property twice.*/ + + if( *pUsed == true ) + { + status = MQTTBadResponse; + } + /*Validate the length and decode.*/ + + else if( *pPropertyLength < sizeof( uint16_t ) ) + { + status = MQTTBadResponse; + } + else + { + *pProperty = UINT16_DECODE( pVariableHeader ); + pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ]; + *pUsed = true; + *pPropertyLength -= sizeof( uint16_t ); + } + + *pIndex = pVariableHeader; + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeuint8_t( uint8_t * pProperty, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ) +{ + uint8_t * pVariableHeader = *pIndex; + MQTTStatus_t status = MQTTSuccess; + + /*Protocol error to include the same property twice.*/ + + if( *pUsed == true ) + { + status = MQTTBadResponse; + } + /*Validate the length and decode.*/ + + else if( *pPropertyLength < sizeof( uint8_t ) ) + { + status = MQTTBadResponse; + } + else + { + *pProperty = *pVariableHeader; + pVariableHeader = &pVariableHeader[ sizeof( uint8_t ) ]; + *pUsed = true; + *pPropertyLength -= sizeof( uint8_t ); + + if( *pProperty > 1U ) + { + status = MQTTBadResponse; + } + } + + *pIndex = pVariableHeader; + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeutf_8( const char ** pProperty, + uint16_t * pLength, + size_t * pPropertyLength, + bool * pUsed, + uint8_t ** pIndex ) +{ + uint8_t * pVariableHeader = *pIndex; + MQTTStatus_t status = MQTTSuccess; + + /*Protocol error to include the same property twice.*/ + if( *pUsed == true ) + { + status = MQTTBadResponse; + } + /*Validate the length and decode.*/ + else if( *pPropertyLength < sizeof( uint16_t ) ) + { + status = MQTTBadResponse; + } + else + { + *pLength = UINT16_DECODE( pVariableHeader ); + pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ]; + *pPropertyLength -= sizeof( uint16_t ); + + if( *pPropertyLength < *pLength ) + { + status = MQTTBadResponse; + } + else + { + *pProperty = ( const char * ) pVariableHeader; + pVariableHeader = &pVariableHeader[ *pLength ]; + *pPropertyLength -= *pLength; + *pUsed = true; + } + } + + *pIndex = pVariableHeader; + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeBinaryData( const void ** pProperty, + uint16_t * pLength, + size_t * pPropertyLength, + uint8_t ** pIndex ) +{ + uint8_t * pVariableHeader = *pIndex; + MQTTStatus_t status = MQTTSuccess; + + /*Validate the length and decode.*/ + + if( *pPropertyLength < sizeof( uint16_t ) ) + { + status = MQTTBadResponse; + } + else + { + *pLength = UINT16_DECODE( pVariableHeader ); + pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ]; + *pPropertyLength -= sizeof( uint16_t ); + + if( *pPropertyLength < *pLength ) + { + status = MQTTBadResponse; + } + else + { + *pProperty = pVariableHeader; + pVariableHeader = &pVariableHeader[ *pLength ]; + *pPropertyLength -= *pLength; + } + } + + *pIndex = pVariableHeader; + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t deserializeConnackProperties( MQTTConnectProperties_t * pConnackProperties, + size_t length, + uint8_t * pIndex, + MQTTPropBuilder_t * propBuffer ) +{ + MQTTStatus_t status = MQTTSuccess; + uint8_t * pVariableHeader = pIndex; + size_t propertyLength = length; + bool sessionExpiry = false; + bool serverReceiveMax = false; + bool maxQos = false; + bool retain = false; + bool maxPacket = false; + bool clientId = false; + bool topicAlias = false; + bool wildcard = false; + bool subId = false; + bool sharedsub = false; + bool keepAlive = false; + bool responseInfo = false; + bool serverReference = false; + bool authMethod = false; + bool authData = false; + bool reasonString = false; + + pVariableHeader = &pVariableHeader[ variableLengthEncodedSize( propertyLength ) ]; + + if( propBuffer != NULL ) + { + propBuffer->pBuffer = pVariableHeader; + propBuffer->bufferLength = propertyLength; + } + + /*Decode all the properties received, validate and store them in pConnackProperties.*/ + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pVariableHeader; + pVariableHeader = &pVariableHeader[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_SESSION_EXPIRY_ID: + status = decodeuint32_t( &pConnackProperties->sessionExpiry, &propertyLength, &sessionExpiry, &pVariableHeader ); + break; + + case MQTT_RECEIVE_MAX_ID: + status = decodeuint16_t( &pConnackProperties->serverReceiveMax, &propertyLength, &serverReceiveMax, &pVariableHeader ); + break; + + case MQTT_MAX_QOS_ID: + status = decodeuint8_t( &pConnackProperties->serverMaxQos, &propertyLength, &maxQos, &pVariableHeader ); + break; + + case MQTT_RETAIN_AVAILABLE_ID: + status = decodeuint8_t( &pConnackProperties->retainAvailable, &propertyLength, &retain, &pVariableHeader ); + break; + + case MQTT_MAX_PACKET_SIZE_ID: + status = decodeuint32_t( &pConnackProperties->serverMaxPacketSize, &propertyLength, &maxPacket, &pVariableHeader ); + break; + + case MQTT_ASSIGNED_CLIENT_ID: + status = decodeAndDiscardutf_8( &propertyLength, &clientId, &pVariableHeader ); + break; + + case MQTT_TOPIC_ALIAS_MAX_ID: + status = decodeuint16_t( &pConnackProperties->serverTopicAliasMax, &propertyLength, &topicAlias, &pVariableHeader ); + break; + + case MQTT_REASON_STRING_ID: + status = decodeAndDiscardutf_8( &propertyLength, &reasonString, &pVariableHeader ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pVariableHeader ); + break; + + case MQTT_WILDCARD_ID: + status = decodeuint8_t( &pConnackProperties->isWildcardAvailable, &propertyLength, &wildcard, &pVariableHeader ); + break; + + case MQTT_SUB_AVAILABLE_ID: + status = decodeuint8_t( &pConnackProperties->isSubscriptionIdAvailable, &propertyLength, &subId, &pVariableHeader ); + break; + + case MQTT_SHARED_SUB_ID: + status = decodeuint8_t( &pConnackProperties->isSharedAvailable, &propertyLength, &sharedsub, &pVariableHeader ); + break; + + case MQTT_SERVER_KEEP_ALIVE_ID: + status = decodeuint16_t( &pConnackProperties->serverKeepAlive, &propertyLength, &keepAlive, &pVariableHeader ); + break; + + case MQTT_RESPONSE_INFO_ID: + status = decodeAndDiscardutf_8( &propertyLength, &responseInfo, &pVariableHeader ); + break; + + case MQTT_SERVER_REF_ID: + status = decodeAndDiscardutf_8( &propertyLength, &serverReference, &pVariableHeader ); + break; + + case MQTT_AUTH_METHOD_ID: + status = decodeAndDiscardutf_8( &propertyLength, &authMethod, &pVariableHeader ); + break; + + case MQTT_AUTH_DATA_ID: + status = decodeAndDiscardutf_8( &propertyLength, &authData, &pVariableHeader ); + break; + + /*Protocol error to include any other property id.*/ + default: + status = MQTTBadResponse; + break; + } + } + + if( status == MQTTSuccess ) + { + /*Receive max cannot be 0.*/ + if( ( serverReceiveMax == true ) && ( pConnackProperties->serverReceiveMax == 0U ) ) + { + status = MQTTBadResponse; + } + + /*Maximum packet size cannot be 0.*/ + else if( ( maxPacket == true ) && ( pConnackProperties->serverMaxPacketSize == 0U ) ) + { + status = MQTTBadResponse; + } + /*Protocol error to send response information if the client has not requested it.*/ + else if( ( responseInfo == true ) && ( pConnackProperties->requestResponseInfo == false ) ) + { + status = MQTTBadResponse; + } + else + { + /* Empty else MISRA 15.7 */ + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t logAckResponse( uint8_t reasonCode, + uint16_t packetIdentifier ) +{ + MQTTStatus_t status = MQTTServerRefused; + + /* coverity[misra_c_2012_rule_10_5_violation] */ + switch( ( MQTTSuccessFailReasonCode_t ) reasonCode ) + { + case MQTT_REASON_PUBACK_SUCCESS: + ( void ) packetIdentifier; + status = MQTTSuccess; + break; + + case MQTT_REASON_PUBACK_NO_MATCHING_SUBSCRIBERS: + LogDebug( ( "Publish accepted with packet id %hu: No matching subscribers.", + ( unsigned short ) packetIdentifier ) ); + status = MQTTSuccess; + break; + + case MQTT_REASON_PUBACK_UNSPECIFIED_ERROR: + LogError( ( "Publish refused with packet id %hu: Connection rate exceeded.", + ( unsigned short ) packetIdentifier ) ); + break; + + case MQTT_REASON_PUBACK_IMPLEMENTATION_SPECIFIC_ERROR: + LogError( ( "Publish refused with packet id %hu: The PUBLISH is valid but the receiver is not willing to accept it.", + ( unsigned short ) packetIdentifier ) ); + break; + + case MQTT_REASON_PUBACK_NOT_AUTHORIZED: + LogError( ( "Publish refused with packet id %hu: The PUBLISH is not authorized.", + ( unsigned short ) packetIdentifier ) ); + break; + + case MQTT_REASON_PUBACK_TOPIC_NAME_INVALID: + LogError( ( "Publish refused with packet id %hu: Topic Name not accepted.", + ( unsigned short ) packetIdentifier ) ); + break; + + case MQTT_REASON_PUBACK_PACKET_IDENTIFIER_IN_USE: + LogError( ( "Publish refused with packet id %hu: The Packet Identifier is already in use. ", + ( unsigned short ) packetIdentifier ) ); + break; + + case MQTT_REASON_PUBACK_QUOTA_EXCEEDED: + LogError( ( "Publish refused with packet id %hu: Quota exceeded.", + ( unsigned short ) packetIdentifier ) ); + break; + + case MQTT_REASON_PUBACK_PAYLOAD_FORMAT_INVALID: + LogError( ( "Publish refused with packet id %hu: Payload format indicator is invalid.", + ( unsigned short ) packetIdentifier ) ); + break; + + default: + status = MQTTBadResponse; + break; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t logSimpleAckResponse( uint8_t reasonCode, + uint16_t packetIdentifier ) +{ + MQTTStatus_t status = MQTTServerRefused; + + /* coverity[misra_c_2012_rule_10_5_violation] */ + switch( ( MQTTSuccessFailReasonCode_t ) reasonCode ) + { + case MQTT_REASON_PUBREL_SUCCESS: + ( void ) packetIdentifier; + status = MQTTSuccess; + break; + + case MQTT_REASON_PUBREL_PACKET_IDENTIFIER_NOT_FOUND: + LogError( ( "Publish refused with packet id %hu: Packet identifier invalid.", + ( unsigned short ) packetIdentifier ) ); + status = MQTTServerRefused; + break; + + default: + status = MQTTBadResponse; + break; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t decodeAckProperties( MQTTPropBuilder_t * propBuffer, + uint8_t * pIndex, + size_t remainingLength ) +{ + size_t propertyLength = 0U; + MQTTStatus_t status = MQTTSuccess; + uint8_t * pLocalIndex = pIndex; + bool reasonString = false; + + /*Decode the property length*/ + status = decodeVariableLength( pLocalIndex, &propertyLength ); + + if( status == MQTTSuccess ) + { + pLocalIndex = &pLocalIndex[ variableLengthEncodedSize( propertyLength ) ]; + + /*Validate the remaining length.*/ + if( remainingLength != ( propertyLength + variableLengthEncodedSize( propertyLength ) + 3U ) ) + { + status = MQTTBadResponse; + } + } + + if( propBuffer != NULL ) + { + propBuffer->pBuffer = pLocalIndex; + propBuffer->bufferLength = propertyLength; + } + + if( ( status == MQTTSuccess ) && ( propBuffer != NULL ) ) + { + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + /*Decode the property id.*/ + uint8_t propertyId = *pLocalIndex; + pLocalIndex = &pLocalIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_REASON_STRING_ID: + status = decodeAndDiscardutf_8( &propertyLength, &reasonString, &pLocalIndex ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pLocalIndex ); + break; + + default: + status = MQTTBadResponse; + break; + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t validateDisconnectResponse( uint8_t reasonCode, + bool incoming ) +{ + MQTTStatus_t status; + + /*Validate the reason code.*/ + /* coverity[misra_c_2012_rule_10_5_violation] */ + switch( ( MQTTSuccessFailReasonCode_t ) reasonCode ) + { + case MQTT_REASON_DISCONNECT_DISCONNECT_WITH_WILL_MESSAGE: + + if( incoming == true ) + { + status = MQTTBadResponse; + } + else + { + status = MQTTSuccess; + } + + break; + + case MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION: + case MQTT_REASON_DISCONNECT_UNSPECIFIED_ERROR: + case MQTT_REASON_DISCONNECT_MALFORMED_PACKET: + case MQTT_REASON_DISCONNECT_PROTOCOL_ERROR: + case MQTT_REASON_DISCONNECT_IMPLEMENTATION_SPECIFIC_ERROR: + case MQTT_REASON_DISCONNECT_TOPIC_NAME_INVALID: + case MQTT_REASON_DISCONNECT_RECEIVE_MAXIMUM_EXCEEDED: + case MQTT_REASON_DISCONNECT_TOPIC_ALIAS_INVALID: + case MQTT_REASON_DISCONNECT_PACKET_TOO_LARGE: + case MQTT_REASON_DISCONNECT_MESSAGE_RATE_TOO_HIGH: + case MQTT_REASON_DISCONNECT_QUOTA_EXCEEDED: + case MQTT_REASON_DISCONNECT_ADMINISTRATIVE_ACTION: + case MQTT_REASON_DISCONNECT_PAYLOAD_FORMAT_INVALID: + status = MQTTSuccess; + break; + + case MQTT_REASON_DISCONNECT_NOT_AUTHORIZED: + case MQTT_REASON_DISCONNECT_SERVER_BUSY: + case MQTT_REASON_DISCONNECT_SERVER_SHUTTING_DOWN: + case MQTT_REASON_DISCONNECT_KEEP_ALIVE_TIMEOUT: + case MQTT_REASON_DISCONNECT_SESSION_TAKEN_OVER: + case MQTT_REASON_DISCONNECT_TOPIC_FILTER_INVALID: + case MQTT_REASON_DISCONNECT_RETAIN_NOT_SUPPORTED: + case MQTT_REASON_DISCONNECT_QOS_NOT_SUPPORTED: + case MQTT_REASON_DISCONNECT_USE_ANOTHER_SERVER: + case MQTT_REASON_DISCONNECT_SERVER_MOVED: + case MQTT_REASON_DISCONNECT_MAXIMUM_CONNECT_TIME: + case MQTT_REASON_DISCONNECT_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED: + case MQTT_REASON_DISCONNECT_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED: + case MQTT_REASON_DISCONNECT_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED: + case MQTT_REASON_DISCONNECT_BAD_AUTHENTICATION_METHOD: + + if( incoming == true ) + { + status = MQTTSuccess; + } + else + { + status = MQTTBadParameter; + } + + break; + + default: + status = MQTTBadResponse; + break; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static uint8_t * encodeBinaryData( uint8_t * pDestination, + const void * pSource, + uint16_t sourceLength ) +{ + uint8_t * pBuffer = NULL; + + /* Typecast const char * typed source buffer to const uint8_t *. + * This is to use same type buffers in memcpy. */ + const uint8_t * pSourceBuffer = ( const uint8_t * ) pSource; + + assert( pDestination != NULL ); + assert( pSource != NULL ); + + pBuffer = pDestination; + + /* The first byte of a UTF-8 string is the high byte of the string length. */ + *pBuffer = UINT16_HIGH_BYTE( sourceLength ); + pBuffer++; + + /* The second byte of a UTF-8 string is the low byte of the string length. */ + *pBuffer = UINT16_LOW_BYTE( sourceLength ); + pBuffer++; + + /* Copy the string into pBuffer. */ + + ( void ) memcpy( pBuffer, pSourceBuffer, sourceLength ); + + /* Return the pointer to the end of the encoded string. */ + pBuffer = &pBuffer[ sourceLength ]; + + return pBuffer; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t deserializePublishProperties( MQTTPublishInfo_t * pPublishInfo, + MQTTPropBuilder_t * propBuffer, + uint8_t * pIndex, + uint16_t topicAliasMax ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pLocalIndex = pIndex; + size_t subscriptionId; + bool contentType = false; + bool messageExpiryInterval = false; + bool responseTopic = false; + bool topicAlias = false; + bool payloadFormatIndicator = false; + bool correlationData = false; + uint16_t topicAliasVal; + + /*Decode Property Length */ + + status = decodeVariableLength( pLocalIndex, &propertyLength ); + + if( status == MQTTSuccess ) + { + pLocalIndex = &pLocalIndex[ variableLengthEncodedSize( propertyLength ) ]; + } + + pPublishInfo->propertyLength = propertyLength; + + if( propBuffer != NULL ) + { + /* Buffer is valid and large enough. Set its actual length. */ + propBuffer->bufferLength = propertyLength; + propBuffer->pBuffer = pLocalIndex; + } + + if( ( status == MQTTSuccess ) && ( propBuffer != NULL ) ) + { + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pLocalIndex; + pLocalIndex = &pLocalIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_PAYLOAD_FORMAT_ID: + status = decodeAndDiscard_uint8( &propertyLength, &payloadFormatIndicator, &pLocalIndex ); + break; + + case MQTT_TOPIC_ALIAS_ID: + status = decodeuint16_t( &topicAliasVal, &propertyLength, &topicAlias, &pLocalIndex ); + break; + + case MQTT_RESPONSE_TOPIC_ID: + status = decodeAndDiscardutf_8( &propertyLength, &responseTopic, &pLocalIndex ); + break; + + case MQTT_CORRELATION_DATA_ID: + status = decodeAndDiscardutf_8( &propertyLength, &correlationData, &pLocalIndex ); + break; + + case MQTT_MSG_EXPIRY_ID: + status = decodeAndDiscard_uint32( &propertyLength, &messageExpiryInterval, &pLocalIndex ); + break; + + case MQTT_CONTENT_TYPE_ID: + status = decodeAndDiscardutf_8( &propertyLength, &contentType, &pLocalIndex ); + break; + + case MQTT_SUBSCRIPTION_ID_ID: + status = decodeVariableLength( pLocalIndex, &subscriptionId ); + + if( status == MQTTSuccess ) + { + pLocalIndex = &pLocalIndex[ variableLengthEncodedSize( subscriptionId ) ]; + propertyLength -= variableLengthEncodedSize( subscriptionId ); + } + + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pLocalIndex ); + break; + + default: + status = MQTTBadResponse; + break; + } + } + } + + if( ( status == MQTTSuccess ) && ( topicAlias == true ) ) + { + if( topicAliasMax < topicAliasVal ) + { + status = MQTTBadParameter; + LogError( ( "Topic Alias greater than Topic Alias Max. " ) ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket, + uint16_t * pPacketId, + MQTTPublishInfo_t * pPublishInfo, + MQTTPropBuilder_t * propBuffer, + uint16_t topicAliasMax ) +{ + MQTTStatus_t status = MQTTSuccess; + uint8_t * pVariableHeader = NULL; + const uint8_t * pPacketIdentifierHigh = NULL; + uint8_t * pIndex = NULL; + + assert( pIncomingPacket != NULL ); + assert( pPacketId != NULL ); + assert( pPublishInfo != NULL ); + assert( pIncomingPacket->pRemainingData != NULL ); + + pVariableHeader = pIncomingPacket->pRemainingData; + pIndex = pVariableHeader; + /* The flags are the lower 4 bits of the first byte in PUBLISH. */ + status = processPublishFlags( ( pIncomingPacket->type & 0x0FU ), pPublishInfo ); + + if( status == MQTTSuccess ) + { + /* Sanity checks for "Remaining length". A QoS 0 PUBLISH must have a remaining + * length of at least 3 to accommodate topic name length (2 bytes) and topic + * name (at least 1 byte). A QoS 1 or 2 PUBLISH must have a remaining length of + * at least 5 for the packet identifier in addition to the topic name length and + * topic name. */ + status = checkPublishRemainingLength( pIncomingPacket->remainingLength, + pPublishInfo->qos, + 4U ); + } + + if( status == MQTTSuccess ) + { + /* Extract the topic name starting from the first byte of the variable header. + * The topic name string starts at byte 3 in the variable header. */ + pPublishInfo->topicNameLength = UINT16_DECODE( pVariableHeader ); + pIndex = &pIndex[ sizeof( uint16_t ) ]; + + /* Sanity checks for topic name length and "Remaining length". The remaining + * length must be at least as large as the variable length header. */ + status = checkPublishRemainingLength( pIncomingPacket->remainingLength, + pPublishInfo->qos, + pPublishInfo->topicNameLength + sizeof( uint16_t ) + sizeof( uint8_t ) ); + pIndex = &pIndex[ pPublishInfo->topicNameLength ]; + } + + if( status == MQTTSuccess ) + { + /* Parse the topic. */ + pPublishInfo->pTopicName = ( char * ) ( &pVariableHeader[ sizeof( uint16_t ) ] ); + LogDebug( ( "Topic name length: %hu.", ( unsigned short ) pPublishInfo->topicNameLength ) ); + + /* Extract the packet identifier for QoS 1 or 2 PUBLISH packets. Packet + * identifier starts immediately after the topic name. */ + pPacketIdentifierHigh = ( const uint8_t * ) &pPublishInfo->pTopicName[ pPublishInfo->topicNameLength ]; + + if( pPublishInfo->qos > MQTTQoS0 ) + { + *pPacketId = UINT16_DECODE( pPacketIdentifierHigh ); + + LogDebug( ( "Packet identifier %hu.", + ( unsigned short ) *pPacketId ) ); + + /* Packet identifier cannot be 0. */ + if( *pPacketId == 0U ) + { + LogError( ( "Packet identifier cannot be 0." ) ); + status = MQTTBadResponse; + } + + if( status == MQTTSuccess ) + { + pIndex = &pIndex[ sizeof( uint16_t ) ]; + } + } + } + + /* insert code for properties here, maybe make a new function - */ + if( status == MQTTSuccess ) + { + status = deserializePublishProperties( pPublishInfo, propBuffer, pIndex, topicAliasMax ); + pIndex = &pIndex[ variableLengthEncodedSize( pPublishInfo->propertyLength ) ]; + pIndex = &pIndex[ pPublishInfo->propertyLength ]; + } + + if( status == MQTTSuccess ) + { + /* Calculate the length of the payload. QoS 1 or 2 PUBLISH packets contain + * a packet identifier, but QoS 0 PUBLISH packets do not. */ + pPublishInfo->payloadLength = pIncomingPacket->remainingLength - pPublishInfo->topicNameLength - sizeof( uint16_t ) - pPublishInfo->propertyLength - variableLengthEncodedSize( pPublishInfo->propertyLength ); + + if( pPublishInfo->qos != MQTTQoS0 ) + { + /* Two more bytes for the packet identifier. */ + pPublishInfo->payloadLength -= sizeof( uint16_t ); + } + + /* Set payload if it exists. */ + + pPublishInfo->pPayload = ( pPublishInfo->payloadLength != 0U ) ? pIndex : NULL; + + LogDebug( ( "Payload length %lu.", + ( unsigned long ) pPublishInfo->payloadLength ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t updateContextWithConnectProps( const MQTTPropBuilder_t * pPropBuilder, + MQTTConnectProperties_t * pConnectProperties ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( pPropBuilder == NULL ) + { + LogError( ( "pPropBuilder cannot be NULL." ) ); + status = MQTTBadParameter; + } + else if( pPropBuilder->pBuffer == NULL ) + { + LogError( ( "pPropBuilder->pBuffer cannot be NULL." ) ); + status = MQTTBadParameter; + } + else if( pConnectProperties == NULL ) + { + LogError( ( "pConnectProperties cannot be NULL." ) ); + status = MQTTBadParameter; + } + else + { + bool maxPacket = false; + bool sessionExpiry = false; + bool receiveMax = false; + bool topicAlias = false; + size_t propertyLength = 0U; + uint8_t * pIndex; + + propertyLength = pPropBuilder->currentIndex; + pIndex = pPropBuilder->pBuffer; /*Pointer to the buffer*/ + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pIndex; + bool used = false; + pIndex = &pIndex[ 1 ]; + propertyLength--; + + switch( propertyId ) + { + case MQTT_SESSION_EXPIRY_ID: + status = decodeuint32_t( &pConnectProperties->sessionExpiry, &propertyLength, &sessionExpiry, &pIndex ); + break; + + case MQTT_RECEIVE_MAX_ID: + status = decodeuint16_t( &pConnectProperties->receiveMax, &propertyLength, &receiveMax, &pIndex ); + break; + + case MQTT_MAX_PACKET_SIZE_ID: + status = decodeuint32_t( &pConnectProperties->maxPacketSize, &propertyLength, &maxPacket, &pIndex ); + break; + + case MQTT_TOPIC_ALIAS_MAX_ID: + status = decodeuint16_t( &pConnectProperties->topicAliasMax, &propertyLength, &topicAlias, &pIndex ); + break; + + case MQTT_REQUEST_PROBLEM_ID: + case MQTT_REQUEST_RESPONSE_ID: + status = decodeAndDiscard_uint8( &propertyLength, &used, &pIndex ); + break; + + case MQTT_AUTH_DATA_ID: + case MQTT_AUTH_METHOD_ID: + status = decodeAndDiscardutf_8( &propertyLength, &used, &pIndex ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pIndex ); + break; + + default: + status = MQTTBadParameter; + break; + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ValidatePublishParams( const MQTTPublishInfo_t * pPublishInfo, + uint8_t retainAvailable, + uint8_t maxQos, + uint16_t topicAlias, + uint32_t maxPacketSize ) +{ + MQTTStatus_t status; + + if( pPublishInfo == NULL ) + { + LogError( ( "Argument cannot be NULL: pPublishInfo=%p ", + ( void * ) pPublishInfo + ) ); + status = MQTTBadParameter; + } + else if( ( pPublishInfo->retain == true ) && ( retainAvailable == 0U ) ) + { + LogError( ( "Retain is not available." ) ); + status = MQTTBadParameter; + } + else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( maxQos == 0U ) ) + { + LogError( ( "Qos value = %hu is not allowed by the server ", + ( unsigned short ) pPublishInfo->qos ) ); + status = MQTTBadParameter; + } + else if( ( topicAlias == 0U ) && ( pPublishInfo->topicNameLength == 0U ) ) + { + LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, " + "topicNameLength=%hu.", + ( void * ) pPublishInfo->pTopicName, + ( unsigned short ) pPublishInfo->topicNameLength ) ); + status = MQTTBadParameter; + } + else if( ( pPublishInfo->pTopicName == NULL ) && ( pPublishInfo->topicNameLength != 0U ) ) + { + LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, " + "topicNameLength=%hu.", + ( void * ) pPublishInfo->pTopicName, + ( unsigned short ) pPublishInfo->topicNameLength ) ); + status = MQTTBadParameter; + } + else if( maxPacketSize == 0U ) + { + status = MQTTBadParameter; + } + else + { + status = MQTTSuccess; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ValidatePublishProperties( uint16_t serverTopicAliasMax, + const MQTTPropBuilder_t * propBuilder, + uint16_t * topicAlias ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pLocalIndex = NULL; + bool topicAliasBool = false; + + if( ( propBuilder == NULL ) || ( propBuilder->pBuffer == NULL ) ) + { + status = MQTTBadParameter; + } + else if( topicAlias == NULL ) + { + status = MQTTBadParameter; + } + else + { + propertyLength = propBuilder->currentIndex; + pLocalIndex = propBuilder->pBuffer; + } + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pLocalIndex; + bool used = false; + pLocalIndex = &pLocalIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_PAYLOAD_FORMAT_ID: + status = decodeAndDiscard_uint8( &propertyLength, &used, &pLocalIndex ); + break; + + case MQTT_MSG_EXPIRY_ID: + status = decodeAndDiscard_uint32( &propertyLength, &used, &pLocalIndex ); + break; + + case MQTT_CONTENT_TYPE_ID: + case MQTT_CORRELATION_DATA_ID: + case MQTT_RESPONSE_TOPIC_ID: + status = decodeAndDiscardutf_8( &propertyLength, &used, &pLocalIndex ); + break; + + case MQTT_TOPIC_ALIAS_ID: + status = decodeuint16_t( topicAlias, &propertyLength, &topicAliasBool, &pLocalIndex ); + + if( ( status == MQTTSuccess ) && ( serverTopicAliasMax < *topicAlias ) ) + { + LogError( ( "Protocol Error: Topic Alias greater than Topic Alias Max" ) ); + status = MQTTBadParameter; + } + + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pLocalIndex ); + break; + + default: + status = MQTTBadParameter; + break; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ValidateSubscribeProperties( uint8_t isSubscriptionIdAvailable, + const MQTTPropBuilder_t * propBuilder ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pLocalIndex = NULL; + size_t subscriptionId = 0; + + if( ( propBuilder == NULL ) || ( propBuilder->pBuffer == NULL ) ) + { + status = MQTTBadParameter; + } + else + { + propertyLength = propBuilder->currentIndex; + pLocalIndex = propBuilder->pBuffer; + } + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pLocalIndex; + pLocalIndex = &pLocalIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_SUBSCRIPTION_ID_ID: + + status = decodeVariableLength( pLocalIndex, &subscriptionId ); + + if( status == MQTTSuccess ) + { + propertyLength -= variableLengthEncodedSize( subscriptionId ); + + if( isSubscriptionIdAvailable == 0U ) + { + LogError( ( "Protocol Error : Subscription Id not available" ) ); + status = MQTTBadParameter; + } + } + + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pLocalIndex ); + break; + + default: + status = MQTTBadParameter; + break; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +static MQTTStatus_t deserializeSubackProperties( MQTTPropBuilder_t * propBuffer, + uint8_t * pIndex, + size_t * pSubackPropertyLength ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pLocalIndex = pIndex; + const char * pReasonString; + uint16_t reasonStringLength; + bool reasonString = false; + + status = decodeVariableLength( pLocalIndex, &propertyLength ); + *pSubackPropertyLength = propertyLength; + + if( status == MQTTSuccess ) + { + pLocalIndex = &pLocalIndex[ variableLengthEncodedSize( propertyLength ) ]; + } + + if( propBuffer != NULL ) + { + propBuffer->bufferLength = propertyLength; + propBuffer->pBuffer = pLocalIndex; + } + + if( ( status == MQTTSuccess ) && ( propBuffer != NULL ) ) + { + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + /** Decode propertyId -> reason string if or user property id*/ + uint8_t propertyId = *pLocalIndex; + pLocalIndex = &pLocalIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_REASON_STRING_ID: + status = decodeutf_8( &pReasonString, &reasonStringLength, &propertyLength, &reasonString, &pLocalIndex ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pLocalIndex ); + break; + + default: + status = MQTTBadResponse; + break; + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket, + uint16_t * pPacketId, + bool * pSessionPresent, + MQTTReasonCodeInfo_t * pReasonCode, + bool requestProblem, + uint32_t maxPacketSize, + MQTTPropBuilder_t * propBuffer, + MQTTConnectProperties_t * pConnectProperties ) +{ + MQTTStatus_t status = MQTTSuccess; + bool skipMaxPacketSizeCheck = false; + + if( ( pIncomingPacket != NULL ) && ( ( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) || ( pIncomingPacket->type == MQTT_PACKET_TYPE_PINGRESP ) ) ) + { + skipMaxPacketSizeCheck = true; + } + + if( pIncomingPacket == NULL ) + { + LogError( ( "pIncomingPacket cannot be NULL." ) ); + status = MQTTBadParameter; + } + + /* Pointer for packet identifier cannot be NULL for packets other than + * CONNACK and PINGRESP. */ + else if( ( pPacketId == NULL ) && + ( ( pIncomingPacket->type != MQTT_PACKET_TYPE_CONNACK ) && + ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) ) + { + LogError( ( "pPacketId cannot be NULL for packet type %02x.", + ( unsigned int ) pIncomingPacket->type ) ); + status = MQTTBadParameter; + } + /* Pointer for session present cannot be NULL for CONNACK. */ + else if( ( pSessionPresent == NULL ) && + ( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) ) + { + LogError( ( "pSessionPresent cannot be NULL for CONNACK packet." ) ); + status = MQTTBadParameter; + } + else if( ( pConnectProperties == NULL ) && ( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) ) + { + LogError( ( "pConnectProperties cannot be NULL for CONNACK packet." ) ); + status = MQTTBadParameter; + } + + /* Pointer for remaining data cannot be NULL for packets other + * than PINGRESP. */ + else if( ( pIncomingPacket->pRemainingData == NULL ) && + ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) + { + LogError( ( "Remaining data of incoming packet is NULL." ) ); + status = MQTTBadParameter; + } + /*Max packet size cannot be 0.*/ + else if( ( maxPacketSize == 0U ) && ( skipMaxPacketSizeCheck != true ) ) + { + status = MQTTBadParameter; + } + else if( ( ( pIncomingPacket->remainingLength + variableLengthEncodedSize( pIncomingPacket->remainingLength ) + 1U ) > maxPacketSize ) && ( skipMaxPacketSizeCheck != true ) ) + { + LogError( ( "Packet Size cannot be greater than max packet size. " ) ); + status = MQTTBadResponse; + } + else + { + /* Make sure response packet is a valid ack. */ + switch( pIncomingPacket->type ) + { + case MQTT_PACKET_TYPE_CONNACK: + status = deserializeConnack( pConnectProperties, pIncomingPacket, pSessionPresent, propBuffer ); + break; + + case MQTT_PACKET_TYPE_PUBACK: + case MQTT_PACKET_TYPE_PUBREC: + status = deserializeSimpleAck( pIncomingPacket, pPacketId, pReasonCode, requestProblem, propBuffer ); + + if( ( status == MQTTSuccess ) && ( pIncomingPacket->remainingLength > 2U ) ) + { + status = logAckResponse( *pReasonCode->reasonCode, *pPacketId ); + } + + break; + + case MQTT_PACKET_TYPE_PUBREL: + case MQTT_PACKET_TYPE_PUBCOMP: + status = deserializeSimpleAck( pIncomingPacket, pPacketId, pReasonCode, requestProblem, propBuffer ); + + if( ( status == MQTTSuccess ) && ( pIncomingPacket->remainingLength > 2U ) ) + { + status = logSimpleAckResponse( *pReasonCode->reasonCode, *pPacketId ); + } + + break; + + case MQTT_PACKET_TYPE_SUBACK: + case MQTT_PACKET_TYPE_UNSUBACK: + status = deserializeSuback( pIncomingPacket, pPacketId, pReasonCode, propBuffer ); + break; + + case MQTT_PACKET_TYPE_PINGRESP: + status = deserializePingresp( pIncomingPacket ); + break; + + /* Any other packet type is invalid. */ + default: + LogError( ( "Function called with unknown packet type:(%02x).", + ( unsigned int ) pIncomingPacket->type ) ); + status = MQTTBadResponse; + break; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +uint8_t * MQTT_SerializeAckFixed( uint8_t * pIndex, + uint8_t packetType, + uint16_t packetId, + size_t remainingLength, + MQTTSuccessFailReasonCode_t reasonCode ) +{ + uint8_t * pIndexLocal = pIndex; + + /* The first byte in the publish ack packet is the control packet type. */ + *pIndexLocal = packetType; + pIndexLocal++; + /*After the packet type fixed header has remaining length.*/ + pIndexLocal = encodeVariableLength( pIndexLocal, remainingLength ); + /*Encode the packet id.*/ + pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( packetId ); + pIndexLocal[ 1 ] = UINT16_LOW_BYTE( packetId ); + pIndexLocal = &pIndexLocal[ 2 ]; + /*We are now sending the ack.*/ + *pIndexLocal = ( uint8_t ) reasonCode; + pIndexLocal++; + return pIndexLocal; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_GetAckPacketSize( size_t * pRemainingLength, + size_t * pPacketSize, + uint32_t maxPacketSize, + size_t ackPropertyLength ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t length = 0U; + size_t propertyLength = 0U; + size_t packetSize = 0U; + + propertyLength = ackPropertyLength; + + /*Validate the parameters.*/ + if( ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) ) + { + status = MQTTBadParameter; + } + else if( maxPacketSize == 0U ) + { + status = MQTTBadParameter; + } + else + { + length += MQTT_PUBLISH_ACK_PACKET_SIZE_WITH_REASON; + + length += variableLengthEncodedSize( propertyLength ) + propertyLength; + + if( length > MQTT_MAX_REMAINING_LENGTH ) + { + status = MQTTBadParameter; + LogError( ( "Remaining Length greater than Maximum Remaining Length according to MQTTv5 spec." ) ); + } + else + { + *pRemainingLength = length; + } + } + + if( status == MQTTSuccess ) + { + packetSize = length + 1U + variableLengthEncodedSize( length ); + + if( packetSize > maxPacketSize ) + { + status = MQTTBadParameter; + LogError( ( "Packet size greater than Max Packet Size specified in the CONNACK" ) ); + } + else + { + *pPacketSize = packetSize; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +uint8_t * MQTT_SerializeDisconnectFixed( uint8_t * pIndex, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength ) +{ + uint8_t * pIndexLocal = pIndex; + + assert( pIndex != NULL ); + /* The first byte in the publish ack packet is the control packet type. */ + *pIndexLocal = MQTT_PACKET_TYPE_DISCONNECT; + pIndexLocal++; + /*After the packet type fixed header has remaining length.*/ + pIndexLocal = encodeVariableLength( pIndexLocal, remainingLength ); + /*Encode the reason code.*/ + *pIndexLocal = ( uint8_t ) reasonCode; + pIndexLocal++; + + return pIndexLocal; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_DeserializeDisconnect( const MQTTPacketInfo_t * pPacket, + uint32_t maxPacketSize, + MQTTReasonCodeInfo_t * pDisconnectInfo, + MQTTPropBuilder_t * propBuffer ) +{ + MQTTStatus_t status = MQTTSuccess; + uint8_t * pIndex = NULL; + size_t propertyLength = 0U; + const char * pReasonString; + uint16_t reasonStringLength; + const char * pServerRef; + uint16_t pServerRefLength; + + /*Validate the arguments*/ + if( ( pPacket == NULL ) || ( pPacket->pRemainingData == NULL ) ) + { + status = MQTTBadParameter; + } + else if( ( pDisconnectInfo == NULL ) ) + { + status = MQTTBadParameter; + } + else if( maxPacketSize == 0U ) + { + status = MQTTBadParameter; + } + /*Packet size should not be more than the max allowed by the client.*/ + else if( ( pPacket->remainingLength + variableLengthEncodedSize( pPacket->remainingLength ) + 1U ) > maxPacketSize ) + { + status = MQTTBadResponse; + } + else if( pPacket->remainingLength == 0U ) + { + /*Do nothing*/ + } + else + { + /* Extract the reason code */ + pIndex = pPacket->pRemainingData; + pDisconnectInfo->reasonCode = pIndex; + pDisconnectInfo->reasonCodeLength = 1U; + pIndex++; + /*Validate the reason code.*/ + status = validateDisconnectResponse( *pDisconnectInfo->reasonCode, true ); + } + + if( status == MQTTSuccess ) + { + if( ( pPacket->remainingLength > 1U ) ) + { + /*Extract the property length.*/ + status = decodeVariableLength( pIndex, &propertyLength ); + + if( ( propBuffer == NULL ) && ( propertyLength != 0U ) ) + { + status = MQTTNoMemory; + LogError( ( "Publish Property buffer is NULL but property length is non-zero." ) ); + } + + if( ( status == MQTTSuccess ) && ( propBuffer != NULL ) ) + { + pIndex = &pIndex[ variableLengthEncodedSize( propertyLength ) ]; + propBuffer->bufferLength = propertyLength; + propBuffer->pBuffer = pIndex; + } + + /*Validate the remaining length.*/ + if( pPacket->remainingLength != ( propertyLength + variableLengthEncodedSize( propertyLength ) + 1U ) ) + { + status = MQTTBadResponse; + } + } + } + + if( status == MQTTSuccess ) + { + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + /*Decode the property id.*/ + uint8_t propertyId = *pIndex; + bool reasonString = false; + bool serverRef = false; + pIndex = &pIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + /*Validate the property id and decode accordingly.*/ + switch( propertyId ) + { + case MQTT_REASON_STRING_ID: + status = decodeutf_8( &pReasonString, &reasonStringLength, &propertyLength, &reasonString, &pIndex ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pIndex ); + break; + + case MQTT_SERVER_REF_ID: + status = decodeutf_8( &pServerRef, &pServerRefLength, &propertyLength, &serverRef, &pIndex ); + break; + + default: + status = MQTTBadResponse; + break; + } + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_SubscribeId( MQTTPropBuilder_t * pPropertyBuilder, + size_t subscriptionId ) +{ + MQTTStatus_t status = MQTTSuccess; + uint8_t * pIndex; + + if( subscriptionId == 0U ) + { + LogError( ( "Subscription Id cannot 0 for subscribe properties : Protocol Error " ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_SUBSCRIPTION_ID_POS ) ) + { + LogError( ( "Subscription Id already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + variableLengthEncodedSize( subscriptionId ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add subscription id" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_SUBSCRIPTION_ID_ID; + pIndex++; + pIndex = encodeVariableLength( pIndex, subscriptionId ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ] ); + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_SUBSCRIPTION_ID_POS ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_UserProp( MQTTPropBuilder_t * pPropertyBuilder, + const MQTTUserProperty_t * userProperty ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( userProperty == NULL ) + { + LogError( ( "Arguments cannot be NULL : userProperty=%p.", ( void * ) userProperty ) ); + status = MQTTBadParameter; + } + else if( ( userProperty->pKey == NULL ) || ( userProperty->pValue == NULL ) || ( userProperty->keyLength == 0U ) || ( userProperty->valueLength == 0U ) ) + { + LogError( ( "Arguments cannot be NULL : pUserProperties->userProperty->pKey=%p , pUserProperties->userProperty->pValue=%p, Key Length = %u, Value Length = %u", ( void * ) userProperty->pKey, ( void * ) userProperty->pValue, userProperty->keyLength, userProperty->valueLength ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + userProperty->keyLength + userProperty->valueLength + sizeof( uint16_t ) + sizeof( uint16_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property." ) ); + status = MQTTNoMemory; + } + else + { + const uint8_t * start = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + uint8_t * pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + + *pIndex = MQTT_USER_PROPERTY_ID; + pIndex++; + + /*Encoding key*/ + pIndex = encodeString( pIndex, userProperty->pKey, userProperty->keyLength ); + pIndex = encodeString( pIndex, userProperty->pValue, userProperty->valueLength ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - start ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_UpdateDuplicatePublishFlag( uint8_t * pHeader, + bool set ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( pHeader == NULL ) + { + LogError( ( "Header cannot be NULL" ) ); + status = MQTTBadParameter; + } + else if( ( ( *pHeader ) & 0xF0U ) != MQTT_PACKET_TYPE_PUBLISH ) + { + LogError( ( "Header is not publish packet header" ) ); + status = MQTTBadParameter; + } + else if( set == true ) + { + UINT8_SET_BIT( *pHeader, MQTT_PUBLISH_FLAG_DUP ); + } + else + { + UINT8_CLEAR_BIT( *pHeader, MQTT_PUBLISH_FLAG_DUP ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_SessionExpiry( MQTTPropBuilder_t * pPropertyBuilder, + uint32_t sessionExpiry ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_SESSION_EXPIRY_INTERVAL_POS ) ) + { + LogError( ( "Connect Session Expiry Already Set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint32_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_SESSION_EXPIRY_ID; + pIndex++; + pIndex[ 0 ] = UINT32_GET_BYTE( sessionExpiry, 3 ); + pIndex[ 1 ] = UINT32_GET_BYTE( sessionExpiry, 2 ); + pIndex[ 2 ] = UINT32_GET_BYTE( sessionExpiry, 1 ); + pIndex[ 3 ] = UINT32_GET_BYTE( sessionExpiry, 0 ); + pIndex = &pIndex[ 4 ]; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_SESSION_EXPIRY_INTERVAL_POS ); + pPropertyBuilder->currentIndex += 5U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ConnReceiveMax( MQTTPropBuilder_t * pPropertyBuilder, + uint16_t receiveMax ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( receiveMax == 0U ) || ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_RECEIVE_MAXIMUM_POS ) ) ) + { + LogError( ( "Invalid arguments passed to MQTTPropAdd_ConnReceiveMax." ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint16_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + uint8_t * pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_RECEIVE_MAX_ID; + pIndex++; + pIndex[ 0 ] = UINT16_HIGH_BYTE( receiveMax ); + pIndex[ 1 ] = UINT16_LOW_BYTE( receiveMax ); + pIndex = &pIndex[ 2 ]; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_RECEIVE_MAXIMUM_POS ); + pPropertyBuilder->currentIndex += 3U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ConnMaxPacketSize( MQTTPropBuilder_t * pPropertyBuilder, + uint32_t maxPacketSize ) +{ + MQTTStatus_t status = MQTTSuccess; + uint8_t * pIndex; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( maxPacketSize == 0U ) || ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_MAX_PACKET_SIZE_POS ) ) ) + { + LogError( ( "Max packet size already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint32_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_MAX_PACKET_SIZE_ID; + pIndex++; + pIndex[ 0 ] = UINT32_GET_BYTE( maxPacketSize, 3 ); + pIndex[ 1 ] = UINT32_GET_BYTE( maxPacketSize, 2 ); + pIndex[ 2 ] = UINT32_GET_BYTE( maxPacketSize, 1 ); + pIndex[ 3 ] = UINT32_GET_BYTE( maxPacketSize, 0 ); + pIndex = &pIndex[ 4 ]; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_MAX_PACKET_SIZE_POS ); + pPropertyBuilder->currentIndex += 5U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ConnTopicAliasMax( MQTTPropBuilder_t * pPropertyBuilder, + uint16_t topicAliasMax ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_TOPIC_ALIAS_MAX_POS ) ) + { + LogError( ( "Topic Alias Maximum already set. " ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint16_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_TOPIC_ALIAS_MAX_ID; + pIndex++; + pIndex[ 0 ] = UINT16_HIGH_BYTE( topicAliasMax ); + pIndex[ 1 ] = UINT16_LOW_BYTE( topicAliasMax ); + pIndex = &pIndex[ 2 ]; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_TOPIC_ALIAS_MAX_POS ); + pPropertyBuilder->currentIndex += 3U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ConnRequestRespInfo( MQTTPropBuilder_t * pPropertyBuilder, + bool requestResponseInfo ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_REQUEST_RESPONSE_INFO_POS ) ) + { + LogError( ( "Request Response Info already set." ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_REQUEST_RESPONSE_ID; + pIndex++; + *pIndex = ( requestResponseInfo ? 1U : 0U ); + pIndex++; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_REQUEST_RESPONSE_INFO_POS ); + pPropertyBuilder->currentIndex += 2U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ConnRequestProbInfo( MQTTPropBuilder_t * pPropertyBuilder, + bool requestProblemInfo ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_REQUEST_PROBLEM_INFO_POS ) ) + { + LogError( ( "Request Problem Info already set." ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_REQUEST_RESPONSE_ID; + pIndex++; + *pIndex = ( requestProblemInfo ? 1U : 0U ); + pIndex++; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_REQUEST_PROBLEM_INFO_POS ); + pPropertyBuilder->currentIndex += 2U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ConnAuthMethod( MQTTPropBuilder_t * pPropertyBuilder, + const char * authMethod, + uint16_t authMethodLength ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) || ( authMethod == NULL ) || ( authMethodLength == 0U ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p, authMethod = %p, authMethodLength = %u", ( void * ) pPropertyBuilder, ( void * ) authMethod, authMethodLength ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_AUTHENTICATION_METHOD_POS ) ) + { + LogError( ( "Auth Method already set." ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + authMethodLength + sizeof( uint16_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_AUTH_METHOD_ID; + pIndex++; + pIndex = encodeString( pIndex, authMethod, authMethodLength ); + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_AUTHENTICATION_METHOD_POS ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - ( &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ] ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ConnAuthData( MQTTPropBuilder_t * pPropertyBuilder, + const char * authData, + uint16_t authDataLength ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) || ( authData == NULL ) || ( authDataLength == 0U ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p, authMethod = %p, authMethodLength = %u", ( void * ) pPropertyBuilder, ( void * ) authData, authDataLength ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + authDataLength + sizeof( uint16_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else if( ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_AUTHENTICATION_DATA_POS ) ) || ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_AUTHENTICATION_METHOD_POS ) == false ) ) + { + LogError( ( "Invalid Auth data" ) ); + status = MQTTBadParameter; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_AUTH_DATA_ID; + pIndex++; + pIndex = encodeString( pIndex, authData, authDataLength ); + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_AUTHENTICATION_DATA_POS ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - ( &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ] ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_PubPayloadFormat( MQTTPropBuilder_t * pPropertyBuilder, + bool payloadFormat ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_PAYLOAD_FORMAT_INDICATOR_POS ) ) ) + { + LogError( ( "Payload Format already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_PAYLOAD_FORMAT_ID; + pIndex++; + *pIndex = ( uint8_t ) ( payloadFormat ? 1U : 0U ); + pIndex++; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_PAYLOAD_FORMAT_INDICATOR_POS ); + pPropertyBuilder->currentIndex += 2U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_PubMessageExpiry( MQTTPropBuilder_t * pPropertyBuilder, + uint32_t messageExpiry ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_MESSAGE_EXPIRY_INTERVAL_POS ) ) ) + { + LogError( ( "Message Expiry Interval already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint32_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_MSG_EXPIRY_ID; + pIndex++; + pIndex[ 0 ] = UINT32_GET_BYTE( messageExpiry, 3 ); + pIndex[ 1 ] = UINT32_GET_BYTE( messageExpiry, 2 ); + pIndex[ 2 ] = UINT32_GET_BYTE( messageExpiry, 1 ); + pIndex[ 3 ] = UINT32_GET_BYTE( messageExpiry, 0 ); + pIndex = &pIndex[ 4 ]; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_MESSAGE_EXPIRY_INTERVAL_POS ); + pPropertyBuilder->currentIndex += 5U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_WillDelayInterval( MQTTPropBuilder_t * pPropertyBuilder, + uint32_t willDelayInterval ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_WILL_DELAY_POS ) ) ) + { + LogError( ( "Message Expiry Interval already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint32_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_WILL_DELAY_ID; + pIndex++; + pIndex[ 0 ] = UINT32_GET_BYTE( willDelayInterval, 3 ); + pIndex[ 1 ] = UINT32_GET_BYTE( willDelayInterval, 2 ); + pIndex[ 2 ] = UINT32_GET_BYTE( willDelayInterval, 1 ); + pIndex[ 3 ] = UINT32_GET_BYTE( willDelayInterval, 0 ); + pIndex = &pIndex[ 4 ]; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_WILL_DELAY_POS ); + pPropertyBuilder->currentIndex += 5U; + } + + return status; +} + +MQTTStatus_t MQTTPropAdd_PubTopicAlias( MQTTPropBuilder_t * pPropertyBuilder, + uint16_t topicAlias ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_TOPIC_ALIAS_POS ) ) + { + LogError( ( "TopicAlias already present" ) ); + status = MQTTBadParameter; + } + else if( topicAlias == 0U ) + { + LogError( ( "Topic Alias cannot be 0" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint16_t ) + sizeof( uint8_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_TOPIC_ALIAS_ID; + pIndex++; + pIndex[ 0 ] = UINT16_HIGH_BYTE( topicAlias ); + pIndex[ 1 ] = UINT16_LOW_BYTE( topicAlias ); + pIndex = &pIndex[ 2 ]; + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_TOPIC_ALIAS_POS ); + pPropertyBuilder->currentIndex += 3U; + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_PubResponseTopic( MQTTPropBuilder_t * pPropertyBuilder, + const char * responseTopic, + uint16_t responseTopicLength ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( responseTopic == NULL ) + { + LogError( ( "Arguments cannot be NULL : responseTopic=%p.", ( void * ) responseTopic ) ); + status = MQTTBadParameter; + } + else if( responseTopicLength == 0U ) + { + LogError( ( "Response Topic Length cannot be 0" ) ); + status = MQTTBadParameter; + } + else if( ( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_RESPONSE_TOPIC_POS ) ) ) + { + LogError( ( "Response Topic already set" ) ); + status = MQTTBadParameter; + } + else if( ( strchr( responseTopic, ( int32_t ) '#' ) != NULL ) || ( strchr( responseTopic, ( int32_t ) '+' ) != NULL ) ) + { + LogError( ( "Protocol Error : Response Topic contains wildcards" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + responseTopicLength + sizeof( uint16_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_RESPONSE_TOPIC_ID; + pIndex++; + pIndex = encodeString( pIndex, responseTopic, responseTopicLength ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - ( &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ] ) ); + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_RESPONSE_TOPIC_POS ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_PubCorrelationData( MQTTPropBuilder_t * pPropertyBuilder, + const void * pCorrelationData, + uint16_t correlationLength ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( pCorrelationData == NULL ) + { + LogError( ( "Arguments cannot be NULL : pCorrelationData=%p.", ( void * ) pCorrelationData ) ); + status = MQTTBadParameter; + } + else if( correlationLength == 0U ) + { + LogError( ( "Correlation Data Length cannot be 0" ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_CORRELATION_DATA_POS ) ) + { + LogError( ( "Correlation Data already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + correlationLength + sizeof( uint16_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_CORRELATION_DATA_ID; + pIndex++; + pIndex = encodeBinaryData( pIndex, pCorrelationData, correlationLength ); + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_CORRELATION_DATA_POS ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - ( &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ] ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_PubContentType( MQTTPropBuilder_t * pPropertyBuilder, + const char * contentType, + uint16_t contentTypeLength ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( contentType == NULL ) + { + LogError( ( "Arguments cannot be NULL : contentType=%p.", ( void * ) contentType ) ); + status = MQTTBadParameter; + } + else if( contentTypeLength == 0U ) + { + LogError( ( "Content Type Length cannot be 0" ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_CONTENT_TYPE_POS ) ) + { + LogError( ( "Content type already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + contentTypeLength + sizeof( uint16_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_CONTENT_TYPE_ID; + pIndex++; + pIndex = encodeString( pIndex, contentType, contentTypeLength ); + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_PUBLISH_CONTENT_TYPE_POS ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - ( &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ] ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropAdd_ReasonString( MQTTPropBuilder_t * pPropertyBuilder, + const char * pReasonString, + uint16_t reasonStringLength ) +{ + uint8_t * pIndex; + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) pPropertyBuilder ) ); + status = MQTTBadParameter; + } + else if( pPropertyBuilder->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) pPropertyBuilder->pBuffer ) ); + status = MQTTBadParameter; + } + else if( pReasonString == NULL ) + { + LogError( ( "Arguments cannot be NULL : pReasonString=%p.", ( void * ) pReasonString ) ); + status = MQTTBadParameter; + } + else if( reasonStringLength == 0U ) + { + LogError( ( "Reason String Length cannot be 0" ) ); + status = MQTTBadParameter; + } + else if( UINT32_CHECK_BIT( pPropertyBuilder->fieldSet, MQTT_REASON_STRING_POS ) ) + { + LogError( ( "Reason String already set" ) ); + status = MQTTBadParameter; + } + else if( ( pPropertyBuilder->currentIndex + sizeof( uint8_t ) + reasonStringLength + sizeof( uint16_t ) ) > pPropertyBuilder->bufferLength ) + { + LogError( ( "Buffer too small to add property" ) ); + status = MQTTNoMemory; + } + else + { + pIndex = &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ]; + *pIndex = MQTT_REASON_STRING_ID; + pIndex++; + pIndex = encodeString( pIndex, pReasonString, reasonStringLength ); + UINT32_SET_BIT( pPropertyBuilder->fieldSet, MQTT_REASON_STRING_POS ); + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + pPropertyBuilder->currentIndex += ( size_t ) ( pIndex - ( &pPropertyBuilder->pBuffer[ pPropertyBuilder->currentIndex ] ) ); + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_PubTopicAlias( MQTTPropBuilder_t * propBuffer, + uint16_t * topicAlias ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( topicAlias == NULL ) + { + LogError( ( "Arguments cannot be NULL : topicAlias=%p.", ( void * ) topicAlias ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint16_t( topicAlias, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_PubPayloadFormatIndicator( MQTTPropBuilder_t * propBuffer, + uint8_t * payloadFormat ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( payloadFormat == NULL ) + { + LogError( ( "Arguments cannot be NULL : payloadFormat=%p.", ( void * ) payloadFormat ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint8_t( payloadFormat, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_PubResponseTopic( MQTTPropBuilder_t * propBuffer, + const char ** responseTopic, + uint16_t * responseTopicLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( responseTopic == NULL ) || ( responseTopicLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : responseTopic=%p, responseTopicLength = %p", ( void * ) responseTopic, ( void * ) responseTopicLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( responseTopic, responseTopicLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_PubCorrelationData( MQTTPropBuilder_t * propBuffer, + const void ** correlationData, + uint16_t * correlationLength ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( correlationData == NULL ) || ( correlationLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : responseTopic=%p, responseTopicLength = %p", ( void * ) correlationData, ( void * ) correlationLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeBinaryData( correlationData, correlationLength, &propertyLength, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_PubMessageExpiryInterval( MQTTPropBuilder_t * propBuffer, + uint32_t * msgExpiryInterval ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( msgExpiryInterval == NULL ) + { + LogError( ( "Arguments cannot be NULL : msgExpiryInterval=%p.", ( void * ) msgExpiryInterval ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint32_t( msgExpiryInterval, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_PubContentType( MQTTPropBuilder_t * propBuffer, + const char ** pContentType, + uint16_t * contentTypeLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pContentType == NULL ) || ( contentTypeLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pContentType=%p, contentTypeLength = %p", ( void * ) pContentType, ( void * ) contentTypeLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pContentType, contentTypeLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_PubSubscriptionId( MQTTPropBuilder_t * propBuffer, + size_t * subscriptionId ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( subscriptionId == NULL ) + { + LogError( ( "Arguments cannot be NULL : subscriptionId=%p.", ( void * ) subscriptionId ) ); + status = MQTTBadParameter; + } + else + { + const uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + status = decodeVariableLength( startOfProp, subscriptionId ); + startOfProp = &startOfProp[ variableLengthEncodedSize( *subscriptionId ) ]; + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_SessionExpiry( MQTTPropBuilder_t * propBuffer, + uint32_t * sessionExpiry ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( sessionExpiry == NULL ) + { + LogError( ( "Arguments cannot be NULL : sessionExpiry=%p.", ( void * ) sessionExpiry ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint32_t( sessionExpiry, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnTopicAliasMax( MQTTPropBuilder_t * propBuffer, + uint16_t * topicAliasMax ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( topicAliasMax == NULL ) + { + LogError( ( "Arguments cannot be NULL : topicAliasMax=%p.", ( void * ) topicAliasMax ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint16_t( topicAliasMax, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnReceiveMax( MQTTPropBuilder_t * propBuffer, + uint16_t * receiveMax ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( receiveMax == NULL ) + { + LogError( ( "Arguments cannot be NULL : receiveMax=%p.", ( void * ) receiveMax ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint16_t( receiveMax, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnMaxQos( MQTTPropBuilder_t * propBuffer, + uint8_t * maxQos ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( maxQos == NULL ) + { + LogError( ( "Arguments cannot be NULL : maxQos=%p.", ( void * ) maxQos ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint8_t( maxQos, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnRetainAvailable( MQTTPropBuilder_t * propBuffer, + uint8_t * retainAvailable ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( retainAvailable == NULL ) + { + LogError( ( "Arguments cannot be NULL : retainAvailable=%p.", ( void * ) retainAvailable ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint8_t( retainAvailable, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnMaxPacketSize( MQTTPropBuilder_t * propBuffer, + uint32_t * maxPacketSize ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( maxPacketSize == NULL ) + { + LogError( ( "Arguments cannot be NULL : maxPacketSize=%p.", ( void * ) maxPacketSize ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint32_t( maxPacketSize, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnClientId( MQTTPropBuilder_t * propBuffer, + const char ** pClientId, + uint16_t * clientIdLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pClientId == NULL ) || ( clientIdLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pClientId=%p, clientIdLength = %p", ( void * ) pClientId, ( void * ) clientIdLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pClientId, clientIdLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnWildcard( MQTTPropBuilder_t * propBuffer, + uint8_t * isWildCardAvailable ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( isWildCardAvailable == NULL ) + { + LogError( ( "Arguments cannot be NULL : isWildCardAvailable=%p.", ( void * ) isWildCardAvailable ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint8_t( isWildCardAvailable, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnSubId( MQTTPropBuilder_t * propBuffer, + uint8_t * isSubIdAvailable ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( isSubIdAvailable == NULL ) + { + LogError( ( "Arguments cannot be NULL : isSubIdAvailable=%p.", ( void * ) isSubIdAvailable ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint8_t( isSubIdAvailable, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_UserProp( MQTTPropBuilder_t * propBuffer, + const char ** pUserPropKey, + uint16_t * pUserPropKeyLen, + const char ** pUserPropVal, + uint16_t * pUserPropValLen ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pUserPropKey == NULL ) || ( pUserPropKeyLen == NULL ) || ( pUserPropVal == NULL ) || ( pUserPropValLen == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pUserPropKey = %p , pUserPropKeyLen = %p , pUserPropVal = %p , pUserPropValLen = %p", ( void * ) pUserPropKey, ( void * ) pUserPropKeyLen, ( void * ) pUserPropVal, ( void * ) pUserPropValLen ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pUserPropKey, pUserPropKeyLen, &propertyLength, &propFlag, &startOfProp ); + propFlag = false; + + if( status == MQTTSuccess ) + { + status = decodeutf_8( pUserPropVal, pUserPropValLen, &propertyLength, &propFlag, &startOfProp ); + } + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ReasonString( MQTTPropBuilder_t * propBuffer, + const char ** pReasonString, + uint16_t * reasonStringLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pReasonString == NULL ) || ( reasonStringLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pReasonString=%p, reasonStringLength = %p", ( void * ) pReasonString, ( void * ) reasonStringLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pReasonString, reasonStringLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ServerRef( MQTTPropBuilder_t * propBuffer, + const char ** pServerRef, + uint16_t * serverRefLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pServerRef == NULL ) || ( serverRefLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pServerRef=%p, reasonStringLength = %p", ( void * ) pServerRef, ( void * ) serverRefLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pServerRef, serverRefLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnSharedSubAvailable( MQTTPropBuilder_t * propBuffer, + uint8_t * isSharedSubAvailable ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( isSharedSubAvailable == NULL ) + { + LogError( ( "Arguments cannot be NULL : isSharedSubAvailable= %p", ( void * ) isSharedSubAvailable ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint8_t( isSharedSubAvailable, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnServerKeepAlive( MQTTPropBuilder_t * propBuffer, + uint16_t * serverKeepAlive ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( serverKeepAlive == NULL ) + { + LogError( ( "Arguments cannot be NULL : serverKeepAlive=%p.", ( void * ) serverKeepAlive ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeuint16_t( serverKeepAlive, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnResponseInfo( MQTTPropBuilder_t * propBuffer, + const char ** pResponseInfo, + uint16_t * responseInfoLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pResponseInfo == NULL ) || ( responseInfoLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pResponseInfo=%p, responseInfoLength = %p", ( void * ) pResponseInfo, ( void * ) responseInfoLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pResponseInfo, responseInfoLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnAuthMethod( MQTTPropBuilder_t * propBuffer, + const char ** pAuthMethod, + uint16_t * authMethodLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pAuthMethod == NULL ) || ( authMethodLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pAuthMethod=%p, authMethodLength = %p", ( void * ) pAuthMethod, ( void * ) authMethodLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pAuthMethod, authMethodLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTTPropGet_ConnAuthData( MQTTPropBuilder_t * propBuffer, + const char ** pAuthData, + uint16_t * authDataLength ) +{ + MQTTStatus_t status = MQTTSuccess; + bool propFlag = false; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else if( ( pAuthData == NULL ) || ( authDataLength == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pAuthData=%p, authDataLength = %p", ( void * ) pAuthData, ( void * ) authDataLength ) ); + status = MQTTBadParameter; + } + else + { + uint8_t * startOfProp = &propBuffer->pBuffer[ propBuffer->currentIndex ]; + size_t propertyLength = propBuffer->bufferLength - propBuffer->currentIndex; + status = decodeutf_8( pAuthData, authDataLength, &propertyLength, &propFlag, &startOfProp ); + + if( status == MQTTSuccess ) + { + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */ + /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */ + /* coverity[misra_c_2012_rule_18_2_violation] */ + /* coverity[misra_c_2012_rule_10_8_violation] */ + propBuffer->currentIndex = ( size_t ) ( startOfProp - propBuffer->pBuffer ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_IncomingGetNextProp( MQTTPropBuilder_t * propBuffer, + uint8_t * propertyId ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( ( propBuffer == NULL ) ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder=%p.", ( void * ) propBuffer ) ); + status = MQTTBadParameter; + } + else if( propBuffer->pBuffer == NULL ) + { + LogError( ( "Arguments cannot be NULL : pPropertyBuilder->pBuffer=%p.", ( void * ) propBuffer->pBuffer ) ); + status = MQTTBadParameter; + } + else + { + if( propBuffer->currentIndex < propBuffer->bufferLength ) + { + *propertyId = propBuffer->pBuffer[ propBuffer->currentIndex ]; + propBuffer->currentIndex += 1U; + } + else + { + status = MQTTEndOfProperties; + LogError( ( "End of Property Buffer." ) ); + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ValidateDisconnectProperties( uint32_t connectSessionExpiry, + const MQTTPropBuilder_t * pPropertyBuilder ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pIndex = NULL; + uint32_t sessionExpiry; + + + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + propertyLength = pPropertyBuilder->currentIndex; + pIndex = pPropertyBuilder->pBuffer; + } + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pIndex; + bool used = false; + pIndex = &pIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_SESSION_EXPIRY_ID: + status = decodeuint32_t( &sessionExpiry, &propertyLength, &used, &pIndex ); + + if( status == MQTTSuccess ) + { + if( ( connectSessionExpiry == 0U ) && ( sessionExpiry != 0U ) ) + { + status = MQTTBadParameter; + LogError( ( "Disconnect Session Expiry non-zero while Connect Session Expiry was zero. " ) ); + } + } + + break; + + case MQTT_REASON_STRING_ID: + status = decodeAndDiscardutf_8( &propertyLength, &used, &pIndex ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pIndex ); + break; + + default: + status = MQTTBadParameter; + break; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ValidateUnsubscribeProperties( const MQTTPropBuilder_t * pPropertyBuilder ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pIndex = NULL; + + + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + propertyLength = pPropertyBuilder->currentIndex; + pIndex = pPropertyBuilder->pBuffer; + } + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pIndex; + pIndex = &pIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pIndex ); + break; + + default: + status = MQTTBadParameter; + break; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ValidateWillProperties( const MQTTPropBuilder_t * pPropertyBuilder ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pIndex = NULL; + + + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + propertyLength = pPropertyBuilder->currentIndex; + pIndex = pPropertyBuilder->pBuffer; + } + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pIndex; + bool used = false; + pIndex = &pIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_WILL_DELAY_ID: + status = decodeAndDiscard_uint32( &propertyLength, &used, &pIndex ); + break; + + case MQTT_PAYLOAD_FORMAT_ID: + status = decodeAndDiscard_uint8( &propertyLength, &used, &pIndex ); + break; + + case MQTT_MSG_EXPIRY_ID: + status = decodeAndDiscard_uint32( &propertyLength, &used, &pIndex ); + break; + + case MQTT_CONTENT_TYPE_ID: + case MQTT_RESPONSE_TOPIC_ID: + case MQTT_CORRELATION_DATA_ID: + status = decodeAndDiscardutf_8( &propertyLength, &used, &pIndex ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pIndex ); + break; + + default: + status = MQTTBadParameter; + break; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_ValidatePublishAckProperties( const MQTTPropBuilder_t * pPropertyBuilder ) +{ + MQTTStatus_t status = MQTTSuccess; + size_t propertyLength = 0U; + uint8_t * pIndex = NULL; + + if( ( pPropertyBuilder != NULL ) && ( pPropertyBuilder->pBuffer != NULL ) ) + { + propertyLength = pPropertyBuilder->currentIndex; + pIndex = pPropertyBuilder->pBuffer; + } + + while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) ) + { + uint8_t propertyId = *pIndex; + bool used = false; + pIndex = &pIndex[ 1 ]; + propertyLength -= sizeof( uint8_t ); + + switch( propertyId ) + { + case MQTT_REASON_STRING_ID: + status = decodeAndDiscardutf_8( &propertyLength, &used, &pIndex ); + break; + + case MQTT_USER_PROPERTY_ID: + status = decodeAndDiscard( &propertyLength, &pIndex ); + break; + + default: + status = MQTTBadParameter; + break; + } + } + + return status; +} + +/*-----------------------------------------------------------*/ + +MQTTStatus_t MQTT_PropertyBuilder_Init( MQTTPropBuilder_t * pPropertyBuilder, + uint8_t * buffer, + size_t length ) +{ + MQTTStatus_t status = MQTTSuccess; + + if( ( pPropertyBuilder == NULL ) || ( buffer == NULL ) || ( length == 0U ) ) + { + LogError( ( "Invalid arguments passed to MQTT_PropertyBuilder_Init." ) ); + status = MQTTBadParameter; + } + + if( status == MQTTSuccess ) + { + pPropertyBuilder->pBuffer = buffer; + pPropertyBuilder->currentIndex = 0; + pPropertyBuilder->bufferLength = length; + pPropertyBuilder->fieldSet = 0; /* 0 means no field is set. */ } return status; diff --git a/source/core_mqtt_utils.c b/source/core_mqtt_utils.c new file mode 100644 index 000000000..994cbda17 --- /dev/null +++ b/source/core_mqtt_utils.c @@ -0,0 +1,66 @@ +/* + * coreMQTT + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file core_mqtt_utils.c + * @brief Implements the utility functions in core_mqtt_utils.h. + */ +#include +#include +#include + +/* Include config defaults header to get default values of configs. */ +#include "core_mqtt_config_defaults.h" +#include "core_mqtt_utils.h" + +uint8_t * encodeVariableLength( uint8_t * pDestination, + size_t length ) +{ + uint8_t lengthByte; + uint8_t * pLengthEnd = NULL; + size_t remainingLength = length; + + assert( pDestination != NULL ); + + pLengthEnd = pDestination; + + /* This algorithm is copied from the MQTT v3.1.1 spec. */ + do + { + lengthByte = ( uint8_t ) ( remainingLength % 128U ); + remainingLength = remainingLength / 128U; + + /* Set the high bit of this byte, indicating that there's more data. */ + if( remainingLength > 0U ) + { + UINT8_SET_BIT( lengthByte, 7 ); + } + + /* Output a single encoded byte. */ + *pLengthEnd = lengthByte; + pLengthEnd++; + } while( remainingLength > 0U ); + + return pLengthEnd; +} diff --git a/source/include/core_mqtt.h b/source/include/core_mqtt.h index c4f2357dd..3cf15fce8 100644 --- a/source/include/core_mqtt.h +++ b/source/include/core_mqtt.h @@ -69,6 +69,7 @@ struct MQTTDeserializedInfo; * @brief An opaque structure provided by the library to the #MQTTStorePacketForRetransmit function when using #MQTTStorePacketForRetransmit. */ typedef struct MQTTVec MQTTVec_t; +struct MQTTPropBuilder; /** * @ingroup mqtt_callback_types @@ -93,19 +94,74 @@ typedef uint32_t (* MQTTGetCurrentTimeFunc_t )( void ); /** * @ingroup mqtt_callback_types * @brief Application callback for receiving incoming publishes and incoming - * acks. + * acks, as well as adding properties to outgoing publish acks. * * @note This callback will be called only if packets are deserialized with a * result of #MQTTSuccess or #MQTTServerRefused. The latter can be obtained - * when deserializing a SUBACK, indicating a broker's rejection of a subscribe. + * when deserializing a SUBACK indicating a broker's rejection of a subscribe, + * or a CONNACK indicating a broker's rejection of a connection. * * @param[in] pContext Initialized MQTT context. * @param[in] pPacketInfo Information on the type of incoming MQTT packet. * @param[in] pDeserializedInfo Deserialized information from incoming packet. + * @param[out] pReasonCode Reason code for the incoming packet. + * @param[out] sendPropsBuffer Properties to be sent in the outgoing packet. + * @param[in] getPropsBuffer Properties to be received in the incoming packet. + * + * @note Get optional properties of incoming packets by calling these functions: + * + * + * - Connack Properties: + * - #MQTTPropGet_SessionExpiry + * - #MQTTPropGet_ConnReceiveMax + * - #MQTTPropGet_ConnMaxQos + * - #MQTTPropGet_ConnRetainAvailable + * - #MQTTPropGet_ConnMaxPacketSize + * - #MQTTPropGet_ConnClientId + * - #MQTTPropGet_ConnTopicAliasMax + * - #MQTTPropGet_ReasonString + * - #MQTTPropGet_UserProp + * - #MQTTPropGet_ConnWildcard + * - #MQTTPropGet_ConnSubId + * - #MQTTPropGet_ConnSharedSubAvailable + * - #MQTTPropGet_ConnServerKeepAlive + * - #MQTTPropGet_ConnResponseInfo + * - #MQTTPropGet_ServerRef + * - #MQTTPropGet_ConnAuthMethod + * - #MQTTPropGet_ConnAuthData + * + * - Publish Properties: + * - #MQTTPropGet_PubTopicAlias + * - #MQTTPropGet_PubPayloadFormatIndicator + * - #MQTTPropGet_PubResponseTopic + * - #MQTTPropGet_PubCorrelationData + * - #MQTTPropGet_PubMessageExpiryInterval + * - #MQTTPropGet_PubContentType + * - #MQTTPropGet_PubSubscriptionId + * - #MQTTPropGet_UserProp + * + * - Ack Properties (PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK): + * - #MQTTPropGet_ReasonString + * - #MQTTPropGet_UserProp + * + * - Disconnect Properties: + * - #MQTTPropGet_SessionExpiry + * - #MQTTPropGet_ReasonString + * - #MQTTPropGet_UserProp + * - #MQTTPropGet_ServerRef + * + * @note Add optional properties to outgoing publish ack packets by calling these functions: + * + * - #MQTTPropAdd_UserProp + * - #MQTTPropAdd_ReasonString + * */ typedef void (* MQTTEventCallback_t )( struct MQTTContext * pContext, struct MQTTPacketInfo * pPacketInfo, - struct MQTTDeserializedInfo * pDeserializedInfo ); + struct MQTTDeserializedInfo * pDeserializedInfo, + enum MQTTSuccessFailReasonCode * pReasonCode, + struct MQTTPropBuilder * sendPropsBuffer, + struct MQTTPropBuilder * getPropsBuffer ); /** * @brief User defined callback used to store outgoing publishes. Used to track any publish @@ -223,6 +279,7 @@ typedef struct MQTTPubAckInfo MQTTPublishState_t publishState; /**< @brief The current state of the publish process. */ } MQTTPubAckInfo_t; + /** * @ingroup mqtt_struct_types * @brief A struct representing an MQTT connection. @@ -259,6 +316,11 @@ typedef struct MQTTContext */ MQTTFixedBuffer_t networkBuffer; + /** + * @brief The buffer used to store properties for outgoing ack packets. + */ + MQTTPropBuilder_t ackPropsBuffer; + /** * @brief The next available ID for outgoing MQTT packets. */ @@ -305,6 +367,11 @@ typedef struct MQTTContext uint32_t pingReqSendTimeMs; /**< @brief Timestamp of the last sent PINGREQ. */ bool waitingForPingResp; /**< @brief If the library is currently awaiting a PINGRESP. */ + /** + * @brief Connect and Connack Properties. + */ + MQTTConnectProperties_t connectProperties; + /** * @brief User defined API used to store outgoing publishes. */ @@ -331,6 +398,7 @@ typedef struct MQTTDeserializedInfo uint16_t packetIdentifier; /**< @brief Packet ID of deserialized packet. */ MQTTPublishInfo_t * pPublishInfo; /**< @brief Pointer to deserialized publish info. */ MQTTStatus_t deserializationResult; /**< @brief Return code of deserialization. */ + MQTTReasonCodeInfo_t * pReasonCode; /**< @brief Pointer to deserialized ack info. */ } MQTTDeserializedInfo_t; /** @@ -428,7 +496,8 @@ MQTTStatus_t MQTT_Init( MQTTContext_t * pContext, * publishes. * @param[in] incomingPublishCount Maximum number of records which can be kept in the memory * pointed to by @p pIncomingPublishRecords. - * + * @param[in] pBuffer Pointer to memory which will be used to store properties of outgoing publish-ACKS. + * @param[in] bufferLength Length of the buffer pointed to by @p pBuffer. * @return #MQTTBadParameter if invalid parameters are passed; * #MQTTSuccess otherwise. * @@ -473,7 +542,10 @@ MQTTStatus_t MQTT_Init( MQTTContext_t * pContext, * { * // We do not expect any incoming publishes in this example, therefore the incoming * // publish pointer is NULL and the count is zero. - * status = MQTT_InitStatefulQoS( &mqttContext, outgoingPublishes, outgoingPublishCount, NULL, 0 ); + * // The buffer is used to store properties of outgoing publish-ACKS. + * uint8_t pBuffer[ 500 ]; + * size_t bufferLength = 500 ; + * status = MQTT_InitStatefulQoS( &mqttContext, outgoingPublishes, outgoingPublishCount, NULL, 0 , pBuffer, bufferLength ); * * // Now QoS1 and/or QoS2 publishes can be sent with this context. * } @@ -484,7 +556,9 @@ MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext, MQTTPubAckInfo_t * pOutgoingPublishRecords, size_t outgoingPublishCount, MQTTPubAckInfo_t * pIncomingPublishRecords, - size_t incomingPublishCount ); + size_t incomingPublishCount, + uint8_t * pBuffer, + size_t bufferLength ); /* @[declare_mqtt_initstatefulqos] */ /** @@ -624,6 +698,8 @@ MQTTStatus_t MQTT_CheckConnectStatus( const MQTTContext_t * pContext ); * @param[in] timeoutMs Maximum time in milliseconds to wait for a CONNACK packet. * A zero timeout makes use of the retries for receiving CONNACK as configured with * #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT. + * @param[in] pPropertyBuilder Properties to be sent in the outgoing packet. + * @param[in] pWillPropertyBuilder Will Properties to be sent in the outgoing packet. * @param[out] pSessionPresent This value will be set to true if a previous session * was present; otherwise it will be set to false. It is only relevant if not * establishing a clean session. @@ -660,6 +736,28 @@ MQTTStatus_t MQTT_CheckConnectStatus( const MQTTContext_t * pContext ); * 2 bytes. In the worst case, it can happen that the remaining 2 bytes are never * received and this API will end up spending timeoutMs + transport receive timeout. * + * @note Functions to add optional properties to the CONNECT packet are: + * + * Connect Properties: + * - #MQTTPropAdd_SessionExpiry + * - #MQTTPropAdd_ConnReceiveMax + * - #MQTTPropAdd_ConnMaxPacketSize + * - #MQTTPropAdd_ConnTopicAliasMax + * - #MQTTPropAdd_ConnRequestRespInfo + * - #MQTTPropAdd_ConnRequestProbInfo + * - #MQTTPropAdd_UserProp + * - #MQTTPropAdd_ConnAuthMethod + * - #MQTTPropAdd_ConnAuthData + * + * Will Properties: + * - #MQTTPropAdd_WillDelayInterval + * - #MQTTPropAdd_PubPayloadFormat + * - #MQTTPropAdd_PubMessageExpiry + * - #MQTTPropAdd_PubResponseTopic + * - #MQTTPropAdd_PubCorrelationData + * - #MQTTPropAdd_PubContentType + * - #MQTTPropAdd_UserProp + * * Example * @code{c} * @@ -685,6 +783,15 @@ MQTTStatus_t MQTT_CheckConnectStatus( const MQTTContext_t * pContext ); * connectInfo.userNameLength = strlen( connectInfo.pUserName ); * connectInfo.pPassword = "somePassword"; * connectInfo.passwordLength = strlen( connectInfo.pPassword ); + * // Optional properties to be sent in the CONNECT packet. + * MQTTPropBuilder_t connectPropsBuilder; + * uint8_t connectPropsBuffer[ 100 ]; + * size_t connectPropsBufferLength = sizeof( connectPropsBuffer ); + * status = MQTT_PropertyBuilder_Init( &connectPropsBuilder, connectPropsBuffer, connectPropsBufferLength ); + * + * // Set a property in the connectPropsBuilder + * uint32_t maxPacketSize = 100 ; + * status = MQTTPropAdd_ConnMaxPacketSize(&connectPropsBuilder, maxPacketSize); * * // The last will and testament is optional, it will be published by the broker * // should this client disconnect without sending a DISCONNECT packet. @@ -693,9 +800,17 @@ MQTTStatus_t MQTT_CheckConnectStatus( const MQTTContext_t * pContext ); * willInfo.topicNameLength = strlen( willInfo.pTopicName ); * willInfo.pPayload = "LWT Message"; * willInfo.payloadLength = strlen( "LWT Message" ); + * // Optional Will Properties to be sent in the CONNECT packet. + * MQTTPropBuilder_t willPropsBuilder; + * uint8_t willPropsBuffer[ 100 ]; + * size_t willPropsBufferLength = sizeof( willPropsBuffer ); + * status = MQTT_PropertyBuilder_Init( &willPropsBuilder, willPropsBuffer, willPropsBufferLength ); + * + * // Set a property in the willPropsBuilder + * status = MQTTPropAdd_PubPayloadFormat( &willPropsBuilder, 1); * * // Send the connect packet. Use 100 ms as the timeout to wait for the CONNACK packet. - * status = MQTT_Connect( pContext, &connectInfo, &willInfo, 100, &sessionPresent ); + * status = MQTT_Connect( pContext, &connectInfo, &willInfo, 100, &sessionPresent, &connectPropsBuilder, &willPropsBuilder ); * * if( status == MQTTSuccess ) * { @@ -711,7 +826,9 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, uint32_t timeoutMs, - bool * pSessionPresent ); + bool * pSessionPresent, + const MQTTPropBuilder_t * pPropertyBuilder, + const MQTTPropBuilder_t * pWillPropertyBuilder ); /* @[declare_mqtt_connect] */ /** @@ -723,7 +840,7 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, * @param[in] subscriptionCount The number of elements in @ pSubscriptionList * array. * @param[in] packetId Packet ID generated by #MQTT_GetPacketId. - * + * @param[in] pPropertyBuilder Properties to be sent in the outgoing packet. * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to * hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; @@ -733,6 +850,11 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, * before calling any other API * #MQTTSuccess otherwise. * + * @note Functions to add optional properties to the SUBSCRIBE packet are: + * + * - #MQTTPropAdd_SubscribeId + * - #MQTTPropAdd_UserProp + * * Example * @code{c} * @@ -753,11 +875,18 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, * subscriptionList[ i ].pTopicFilter = filters[ i ]; * subscriptionList[ i ].topicFilterLength = strlen( filters[ i ] ); * } + * // Optional Properties to be sent in the SUBSCRIBE packet + * MQTTPropBuilder_t propertyBuilder; + * uint8_t propertyBuffer[ 100 ]; + * size_t propertyBufferLength = sizeof( propertyBuffer ); + * status = MQTT_PropertyBuilder_Init( &propertyBuilder, propertyBuffer, propertyBufferLength ); + * + * status = MQTTPropAdd_SubscribeId(&propertyBuilder, 1); * * // Obtain a new packet id for the subscription. * packetId = MQTT_GetPacketId( pContext ); * - * status = MQTT_Subscribe( pContext, &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId ); + * status = MQTT_Subscribe( pContext, &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId, propertyBuilder ); * * if( status == MQTTSuccess ) * { @@ -768,21 +897,25 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, * @endcode */ /* @[declare_mqtt_subscribe] */ + MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, - uint16_t packetId ); + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ); + /* @[declare_mqtt_subscribe] */ + /** * @brief Publishes a message to the given topic name. * * @param[in] pContext Initialized MQTT context. * @param[in] pPublishInfo MQTT PUBLISH packet parameters. * @param[in] packetId packet ID generated by #MQTT_GetPacketId. + * @param[in] pPropertyBuilder Properties to be sent in the outgoing packet. * - * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet; - * #MQTTBadParameter if invalid parameters are passed; + * @return #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport write failed; * #MQTTStatusNotConnected if the connection is not established yet * #MQTTStatusDisconnectPending if the user is expected to call MQTT_Disconnect @@ -791,6 +924,16 @@ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, * outgoing publish packet fails * #MQTTSuccess otherwise. * + * @note Functions to add optional properties to the PUBLISH packet are: + * + * - #MQTTPropAdd_PubPayloadFormat + * - #MQTTPropAdd_PubMessageExpiry + * - #MQTTPropAdd_PubTopicAlias + * - #MQTTPropAdd_PubResponseTopic + * - #MQTTPropAdd_PubCorrelationData + * - #MQTTPropAdd_PubContentType + * - #MQTTPropAdd_UserProp + * * Example * @code{c} * @@ -807,11 +950,19 @@ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, * publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); * publishInfo.pPayload = "Hello World!"; * publishInfo.payloadLength = strlen( "Hello World!" ); + * // Optional properties to be sent in the PUBLISH packet. + * MQTTPropBuilder_t propertyBuilder; + * uint8_t propertyBuffer[ 100 ]; + * size_t propertyBufferLength = sizeof( propertyBuffer ); + * status = MQTT_PropertyBuilder_Init( &propertyBuilder, propertyBuffer, propertyBufferLength ); + * + * // Set a property in the propertyBuilder + * status = MQTTPropAdd_PubPayloadFormat( &propertyBuilder, 1); * * // Packet ID is needed for QoS > 0. * packetId = MQTT_GetPacketId( pContext ); * - * status = MQTT_Publish( pContext, &publishInfo, packetId ); + * status = MQTT_Publish( pContext, &publishInfo, packetId, &propertyBuilder ); * * if( status == MQTTSuccess ) * { @@ -823,7 +974,8 @@ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, /* @[declare_mqtt_publish] */ MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, - uint16_t packetId ); + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ); /* @[declare_mqtt_publish] */ /** @@ -873,6 +1025,7 @@ MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext ); * @param[in] pSubscriptionList List of MQTT subscription info. * @param[in] subscriptionCount The number of elements in pSubscriptionList. * @param[in] packetId packet ID generated by #MQTT_GetPacketId. + * @param[in] pPropertyBuilder Properties to be sent in the outgoing packet. * * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to * hold the MQTT packet; @@ -883,6 +1036,10 @@ MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext ); * before calling any other API * #MQTTSuccess otherwise. * + * @note Functions to add optional properties to the UNSUBSCRIBE packet are: + * + * - #MQTTPropAdd_UserProp + * * Example * @code{c} * @@ -906,8 +1063,22 @@ MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext ); * * // Obtain a new packet id for the unsubscribe request. * packetId = MQTT_GetPacketId( pContext ); + * // Optional properties to be sent in the UNSUBSCRIBE packet. + * MQTTPropBuilder_t propertyBuilder; + * uint8_t propertyBuffer[ 100 ]; + * size_t propertyBufferLength = sizeof( propertyBuffer ); + * status = MQTT_PropertyBuilder_Init( &propertyBuilder, propertyBuffer, propertyBufferLength ); + * + * // Set a property in the propertyBuilder + * MQTTUserProperty_t userProperty; + * userProperty.pKey = "key"; + * userProperty.keyLength = strlen( userProperty.pKey ); + * userProperty.pValue = "value"; + * *userProperty.valueLength = strlen( userProperty.pValue ); * - * status = MQTT_Unsubscribe( pContext, &unsubscribeList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId ); + * status = MQTTPropAdd_UserProp( &propertyBuilder,&userProperty); + * + * status = MQTT_Unsubscribe( pContext, &unsubscribeList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId, &propertyBuilder ); * * if( status == MQTTSuccess ) * { @@ -920,23 +1091,57 @@ MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext ); MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, - uint16_t packetId ); + uint16_t packetId, + const MQTTPropBuilder_t * pPropertyBuilder ); /* @[declare_mqtt_unsubscribe] */ /** - * @brief Disconnect an MQTT session. + * @brief Sends MQTT DISCONNECT for a given reason code * * @param[in] pContext Initialized and connected MQTT context. + * @param[in] pPropertyBuilder Properties to be sent in the outgoing packet. + * @param[in] reasonCode Reason code to be sent in the DISCONNECT packet. * * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to * hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport send failed; - * #MQTTStatusNotConnected if the connection is already disconnected * #MQTTSuccess otherwise. + * + * @note Functions to add optional properties to the DISCONNECT packet are: + * + * - #MQTTPropAdd_SessionExpiry + * - #MQTTPropAdd_ReasonString + * - #MQTTPropAdd_UserProp + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * // This context is assumed to be initialized and connected. + * MQTTContext_t * pContext; + * // Optional properties to be sent in the DISCONNECT packet. + * MQTTPropBuilder_t propertyBuilder; + * uint8_t propertyBuffer[ 100 ]; + * size_t propertyBufferLength = sizeof( propertyBuffer ); + * status = MQTT_PropertyBuilder_Init( &propertyBuilder, propertyBuffer, propertyBufferLength ); + * + * // Set a property in the propertyBuilder + * status = MQTTPropAdd_ReasonString( &propertyBuilder, "Disconnecting", 12); + * + * status = MQTT_Disconnect( pContext, &propertyBuilder, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + * + * if( status == MQTTSuccess ) + * { + * // The DISCONNECT packet was sent successfully. The connection is now closed. + * } + * @endcode */ /* @[declare_mqtt_disconnect] */ -MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext ); +MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext, + const MQTTPropBuilder_t * pPropertyBuilder, + MQTTSuccessFailReasonCode_t reasonCode ); /* @[declare_mqtt_disconnect] */ /** @@ -1209,6 +1414,7 @@ MQTTStatus_t MQTT_MatchTopic( const char * pTopicName, * @endcode */ /* @[declare_mqtt_getsubackstatuscodes] */ + MQTTStatus_t MQTT_GetSubAckStatusCodes( const MQTTPacketInfo_t * pSubackPacket, uint8_t ** pPayloadStart, size_t * pPayloadSize ); @@ -1247,6 +1453,21 @@ void MQTT_SerializeMQTTVec( uint8_t * pAllocatedMem, const MQTTVec_t * pVec ); /* @[declare_mqtt_serializemqttvec] */ +/** + * @brief Initialize an MQTTConnectProperties_t. + * + * @note This function initializes the connect properties to default values. + * + * @param[in] pConnectProperties The connect properties to initialize. + * + * @return + * - #MQTTBadParameter if pConnectProperties is NULL. + * - #MQTTSuccess otherwise. + */ +/* @[declare_mqtt_initconnect] */ +MQTTStatus_t MQTT_InitConnect( MQTTConnectProperties_t * pConnectProperties ); +/* @[declare_mqtt_initconnect] */ + /* *INDENT-OFF* */ #ifdef __cplusplus } diff --git a/source/include/core_mqtt_config_defaults.h b/source/include/core_mqtt_config_defaults.h index 3fd79f974..3e489b1ec 100644 --- a/source/include/core_mqtt_config_defaults.h +++ b/source/include/core_mqtt_config_defaults.h @@ -73,7 +73,7 @@ * @brief Maximum number of vectors in subscribe and unsubscribe packet. */ #ifndef MQTT_SUB_UNSUB_MAX_VECTORS - #define MQTT_SUB_UNSUB_MAX_VECTORS ( 4U ) + #define MQTT_SUB_UNSUB_MAX_VECTORS ( 6U ) #endif /** diff --git a/source/include/core_mqtt_serializer.h b/source/include/core_mqtt_serializer.h index 92c3ceeae..fed6cf712 100644 --- a/source/include/core_mqtt_serializer.h +++ b/source/include/core_mqtt_serializer.h @@ -43,6 +43,7 @@ /* *INDENT-ON */ #include "transport_interface.h" +#include "core_mqtt_config_defaults.h" /* MQTT packet types. */ @@ -78,7 +79,10 @@ struct MQTTConnectInfo; struct MQTTSubscribeInfo; struct MQTTPublishInfo; struct MQTTPacketInfo; - +struct MQTTSubscribeProperties ; +struct MQTTConnectProperties; +struct MQTTUserProperty; +struct MQTTAuthInfo; /** * @ingroup mqtt_enum_types * @brief Return codes from MQTT functions. @@ -99,6 +103,8 @@ typedef enum MQTTStatus MQTTNeedMoreBytes, /**< MQTT_ProcessLoop/MQTT_ReceiveLoop has received incomplete data; it should be called again (probably after a delay). */ + MQTTEndOfProperties, + MQTTStatusConnected, /**< MQTT connection is established with the broker. */ MQTTStatusNotConnected, /**< MQTT connection is not established with the broker. */ MQTTStatusDisconnectPending, /**< Transport Interface has failed and MQTT connection needs to be closed. */ @@ -119,6 +125,278 @@ typedef enum MQTTQoS MQTTQoS2 = 2 /**< Delivery exactly once. */ } MQTTQoS_t; +/** + * @ingroup mqtt_enum_types + * @brief MQTT reason codes. + * + * These values are defined in the MQTT 5.0 specification. + */ +typedef enum MQTTSuccessFailReasonCode +{ + /* PUBACK reason codes */ + MQTT_REASON_PUBACK_SUCCESS = 0x00, /**< Publish was successfully received and accepted. */ + MQTT_REASON_PUBACK_NO_MATCHING_SUBSCRIBERS = 0x10, /**< Publish was accepted but there are no subscribers. */ + MQTT_REASON_PUBACK_UNSPECIFIED_ERROR = 0x80, /**< Unspecified error occurred for the PUBACK. */ + MQTT_REASON_PUBACK_IMPLEMENTATION_SPECIFIC_ERROR = 0x83, /**< Implementation specific error for the PUBACK. */ + MQTT_REASON_PUBACK_NOT_AUTHORIZED = 0x87, /**< Client is not authorized to publish. */ + MQTT_REASON_PUBACK_TOPIC_NAME_INVALID = 0x90, /**< Topic name is not valid. */ + MQTT_REASON_PUBACK_PACKET_IDENTIFIER_IN_USE = 0x91, /**< Packet identifier is already in use. */ + MQTT_REASON_PUBACK_QUOTA_EXCEEDED = 0x97, /**< Implementation or system quota exceeded. */ + MQTT_REASON_PUBACK_PAYLOAD_FORMAT_INVALID = 0x99, /**< Payload format is invalid. */ + + /* PUBREC reason codes */ + MQTT_REASON_PUBREC_SUCCESS = 0x00, /**< Publish was successfully received for QoS 2. */ + MQTT_REASON_PUBREC_NO_MATCHING_SUBSCRIBERS = 0x10, /**< Publish received but no matching subscribers. */ + MQTT_REASON_PUBREC_UNSPECIFIED_ERROR = 0x80, /**< Unspecified error occurred for the PUBREC. */ + MQTT_REASON_PUBREC_IMPLEMENTATION_SPECIFIC_ERROR = 0x83, /**< Implementation specific error for the PUBREC. */ + MQTT_REASON_PUBREC_NOT_AUTHORIZED = 0x87, /**< Client is not authorized to publish. */ + MQTT_REASON_PUBREC_TOPIC_NAME_INVALID = 0x90, /**< Topic name is not valid. */ + MQTT_REASON_PUBREC_PACKET_IDENTIFIER_IN_USE = 0x91, /**< Packet identifier is already in use. */ + MQTT_REASON_PUBREC_QUOTA_EXCEEDED = 0x97, /**< Implementation or system quota exceeded. */ + MQTT_REASON_PUBREC_PAYLOAD_FORMAT_INVALID = 0x99, /**< Payload format is invalid. */ + + /* PUBREL reason codes */ + MQTT_REASON_PUBREL_SUCCESS = 0x00, /**< Publish release was successful. */ + MQTT_REASON_PUBREL_PACKET_IDENTIFIER_NOT_FOUND = 0x92, /**< Packet identifier was not found. */ + + /* PUBCOMP reason codes */ + MQTT_REASON_PUBCOMP_SUCCESS = 0x00, /**< Publish complete was successful. */ + MQTT_REASON_PUBCOMP_PACKET_IDENTIFIER_NOT_FOUND = 0x92, /**< Packet identifier was not found. */ + + /* CONNACK reason codes */ + MQTT_REASON_CONNACK_SUCCESS = 0x00, /**< Connection accepted. */ + MQTT_REASON_CONNACK_UNSPECIFIED_ERROR = 0x80, /**< Unspecified error occurred during connection. */ + MQTT_REASON_CONNACK_MALFORMED_PACKET = 0x81, /**< Received packet was malformed. */ + MQTT_REASON_CONNACK_PROTOCOL_ERROR = 0x82, /**< Protocol error occurred. */ + MQTT_REASON_CONNACK_IMPLEMENTATION_SPECIFIC_ERROR = 0x83, /**< Implementation specific error. */ + MQTT_REASON_CONNACK_UNSUPPORTED_PROTOCOL_VERSION = 0x84, /**< Protocol version not supported. */ + MQTT_REASON_CONNACK_CLIENT_IDENTIFIER_NOT_VALID = 0x85, /**< Client identifier is not valid. */ + MQTT_REASON_CONNACK_BAD_USER_NAME_OR_PASSWORD = 0x86, /**< Username or password is malformed. */ + MQTT_REASON_CONNACK_NOT_AUTHORIZED = 0x87, /**< Client is not authorized to connect. */ + MQTT_REASON_CONNACK_SERVER_UNAVAILABLE = 0x88, /**< Server is unavailable. */ + MQTT_REASON_CONNACK_SERVER_BUSY = 0x89, /**< Server is busy. */ + MQTT_REASON_CONNACK_BANNED = 0x8A, /**< Client has been banned. */ + MQTT_REASON_CONNACK_BAD_AUTHENTICATION_METHOD = 0x8C, /**< Authentication method is not supported. */ + MQTT_REASON_CONNACK_TOPIC_NAME_INVALID = 0x90, /**< Topic name is invalid. */ + MQTT_REASON_CONNACK_PACKET_TOO_LARGE = 0x95, /**< Packet size exceeds maximum allowed. */ + MQTT_REASON_CONNACK_QUOTA_EXCEEDED = 0x97, /**< Implementation or system quota exceeded. */ + MQTT_REASON_CONNACK_PAYLOAD_FORMAT_INVALID = 0x99, /**< Payload format is invalid. */ + MQTT_REASON_CONNACK_RETAIN_NOT_SUPPORTED = 0x9A, /**< Retain is not supported. */ + MQTT_REASON_CONNACK_QOS_NOT_SUPPORTED = 0x9B, /**< QoS level is not supported. */ + MQTT_REASON_CONNACK_USE_ANOTHER_SERVER = 0x9C, /**< Client should temporarily use another server. */ + MQTT_REASON_CONNACK_SERVER_MOVED = 0x9D, /**< Client should permanently use another server. */ + MQTT_REASON_CONNACK_CONNECTION_RATE_EXCEEDED = 0x9F, /**< Connection rate limit exceeded. */ + + /* SUBACK reason codes */ + MQTT_REASON_SUBACK_GRANTED_QOS0 = 0x00, /**< Subscription accepted with maximum QoS 0. */ + MQTT_REASON_SUBACK_GRANTED_QOS1 = 0x01, /**< Subscription accepted with maximum QoS 1. */ + MQTT_REASON_SUBACK_GRANTED_QOS2 = 0x02, /**< Subscription accepted with maximum QoS 2. */ + MQTT_REASON_SUBACK_UNSPECIFIED_ERROR = 0x80, /**< Unspecified error occurred for the subscription. */ + MQTT_REASON_SUBACK_IMPLEMENTATION_SPECIFIC_ERROR = 0x83, /**< Implementation specific error. */ + MQTT_REASON_SUBACK_NOT_AUTHORIZED = 0x87, /**< Client is not authorized to subscribe. */ + MQTT_REASON_SUBACK_TOPIC_FILTER_INVALID = 0x8F, /**< Topic filter is not valid. */ + MQTT_REASON_SUBACK_PACKET_IDENTIFIER_IN_USE = 0x91, /**< Packet identifier is already in use. */ + MQTT_REASON_SUBACK_QUOTA_EXCEEDED = 0x97, /**< Implementation or system quota exceeded. */ + MQTT_REASON_SUBACK_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9E, /**< Shared subscriptions are not supported. */ + MQTT_REASON_SUBACK_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 0xA1, /**< Subscription identifiers are not supported. */ + MQTT_REASON_SUBACK_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xA2, /**< Wildcard subscriptions are not supported. */ + + /* UNSUBACK reason codes */ + MQTT_REASON_UNSUBACK_SUCCESS = 0x00, /**< Unsubscribe was successful. */ + MQTT_REASON_UNSUBACK_NO_SUBSCRIPTION_EXISTED = 0x11, /**< No matching subscription existed. */ + MQTT_REASON_UNSUBACK_UNSPECIFIED_ERROR = 0x80, /**< Unspecified error occurred for the unsubscribe. */ + MQTT_REASON_UNSUBACK_IMPLEMENTATION_SPECIFIC_ERROR = 0x83, /**< Implementation specific error. */ + MQTT_REASON_UNSUBACK_NOT_AUTHORIZED = 0x87, /**< Client is not authorized to unsubscribe. */ + MQTT_REASON_UNSUBACK_TOPIC_FILTER_INVALID = 0x8F, /**< Topic filter is not valid. */ + MQTT_REASON_UNSUBACK_PACKET_IDENTIFIER_IN_USE = 0x91, /**< Packet identifier is already in use. */ + + /* DISCONNECT reason codes */ + MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION = 0x00, /**< Normal client-initiated disconnect. */ + MQTT_REASON_DISCONNECT_DISCONNECT_WITH_WILL_MESSAGE = 0x04, /**< Client disconnecting with Will Message. */ + MQTT_REASON_DISCONNECT_UNSPECIFIED_ERROR = 0x80, /**< Unspecified error occurred. */ + MQTT_REASON_DISCONNECT_MALFORMED_PACKET = 0x81, /**< Received packet was malformed. */ + MQTT_REASON_DISCONNECT_PROTOCOL_ERROR = 0x82, /**< Protocol error occurred. */ + MQTT_REASON_DISCONNECT_IMPLEMENTATION_SPECIFIC_ERROR = 0x83, /**< Implementation specific error. */ + MQTT_REASON_DISCONNECT_NOT_AUTHORIZED = 0x87, /**< Client is not authorized. */ + MQTT_REASON_DISCONNECT_SERVER_BUSY = 0x89, /**< Server is busy. */ + MQTT_REASON_DISCONNECT_SERVER_SHUTTING_DOWN = 0x8B, /**< Server is shutting down. */ + MQTT_REASON_DISCONNECT_BAD_AUTHENTICATION_METHOD = 0x8C, /**< Authentication method is invalid. */ + MQTT_REASON_DISCONNECT_KEEP_ALIVE_TIMEOUT = 0x8D, /**< Keep alive timeout occurred. */ + MQTT_REASON_DISCONNECT_SESSION_TAKEN_OVER = 0x8E, /**< Another connection using same client ID. */ + MQTT_REASON_DISCONNECT_TOPIC_FILTER_INVALID = 0x8F, /**< Topic filter is not valid. */ + MQTT_REASON_DISCONNECT_TOPIC_NAME_INVALID = 0x90, /**< Topic name is not valid. */ + MQTT_REASON_DISCONNECT_RECEIVE_MAXIMUM_EXCEEDED = 0x93, /**< Receive maximum value exceeded. */ + MQTT_REASON_DISCONNECT_TOPIC_ALIAS_INVALID = 0x94, /**< Topic alias is invalid. */ + MQTT_REASON_DISCONNECT_PACKET_TOO_LARGE = 0x95, /**< Packet size exceeds maximum allowed. */ + MQTT_REASON_DISCONNECT_MESSAGE_RATE_TOO_HIGH = 0x96, /**< Message rate too high. */ + MQTT_REASON_DISCONNECT_QUOTA_EXCEEDED = 0x97, /**< Implementation or system quota exceeded. */ + MQTT_REASON_DISCONNECT_ADMINISTRATIVE_ACTION = 0x98, /**< Disconnected due to administrative action. */ + MQTT_REASON_DISCONNECT_PAYLOAD_FORMAT_INVALID = 0x99, /**< Payload format is invalid. */ + MQTT_REASON_DISCONNECT_RETAIN_NOT_SUPPORTED = 0x9A, /**< Retain is not supported. */ + MQTT_REASON_DISCONNECT_QOS_NOT_SUPPORTED = 0x9B, /**< QoS level is not supported. */ + MQTT_REASON_DISCONNECT_USE_ANOTHER_SERVER = 0x9C, /**< Client should temporarily use another server. */ + MQTT_REASON_DISCONNECT_SERVER_MOVED = 0x9D, /**< Client should permanently use another server. */ + MQTT_REASON_DISCONNECT_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9E, /**< Shared subscriptions are not supported. */ + MQTT_REASON_DISCONNECT_CONNECTION_RATE_EXCEEDED = 0x9F, /**< Connection rate limit exceeded. */ + MQTT_REASON_DISCONNECT_MAXIMUM_CONNECT_TIME = 0xA0, /**< Maximum connection time authorized exceeded. */ + MQTT_REASON_DISCONNECT_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 0xA1, /**< Subscription identifiers are not supported. */ + MQTT_REASON_DISCONNECT_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xA2, /**< Wildcard subscriptions are not supported. */ + + MQTT_INVALID_REASON_CODE = 0xFF /**< @brief Invalid reason code. */ + +} MQTTSuccessFailReasonCode_t; + + +#define MQTT_SUBSCRIBE_QOS1 ( 0 ) /**< @brief MQTT SUBSCRIBE QoS1 flag. */ +#define MQTT_SUBSCRIBE_QOS2 ( 1 ) /**< @brief MQTT SUBSCRIBE QoS2 flag. */ +#define MQTT_SUBSCRIBE_NO_LOCAL ( 2 ) /**< @brief MQTT SUBSCRIBE no local flag. */ +#define MQTT_SUBSCRIBE_RETAIN_AS_PUBLISHED ( 3 ) /**< @brief MQTT SUBSCRIBE retain as published flag. */ +#define MQTT_SUBSCRIBE_RETAIN_HANDLING1 ( 4 ) /**<@brief MQTT SUBSCRIBE Retain Handling Option 1 */ +#define MQTT_SUBSCRIBE_RETAIN_HANDLING2 ( 5 ) /**<@brief Retain Handling Option 2 -> in core_mqtt_serializer.c */ + +/*CONNECT PROPERTIES*/ + +/** +* @brief Session expiry id. +*/ +#define MQTT_SESSION_EXPIRY_ID ( 0x11 ) + +/** +* @brief Receive maximum id. +*/ +#define MQTT_RECEIVE_MAX_ID ( 0x21 ) + +/** +* @brief Maximum packet size id. +*/ +#define MQTT_MAX_PACKET_SIZE_ID ( 0x27 ) + +/** +* @brief Topic alias size id. +*/ +#define MQTT_TOPIC_ALIAS_MAX_ID ( 0x22 ) + +/** +* @brief Request response id. +*/ +#define MQTT_REQUEST_RESPONSE_ID ( 0x19 ) + +/** +* @brief Request problem id. +*/ +#define MQTT_REQUEST_PROBLEM_ID ( 0x17 ) + +/** +* @brief User property id. +*/ +#define MQTT_USER_PROPERTY_ID ( 0x26 ) + +/** +* @brief Authentication method id. +*/ +#define MQTT_AUTH_METHOD_ID ( 0x15 ) + +/** +* @brief Authentication data id. +*/ +#define MQTT_AUTH_DATA_ID ( 0x16 ) + +/*Publish PROPERTIES*/ + +/** +* @brief Will delay id. +*/ +#define MQTT_WILL_DELAY_ID ( 0x18 ) + +/** +* @brief Payload format id. +*/ +#define MQTT_PAYLOAD_FORMAT_ID ( 0x01 ) + +/** +* @brief Message Expiry id. +*/ +#define MQTT_MSG_EXPIRY_ID ( 0x02 ) + +/** +* @brief Content type id. +*/ +#define MQTT_CONTENT_TYPE_ID ( 0x03 ) + +/** +* @brief Response topic id. +*/ +#define MQTT_RESPONSE_TOPIC_ID ( 0x08 ) + +/** +* @brief Correlation data id. +*/ +#define MQTT_CORRELATION_DATA_ID ( 0x09 ) + +/** +* @brief Topic alias id. +*/ +#define MQTT_TOPIC_ALIAS_ID ( 0x23 ) + +/*CONNACK PROPERTIES*/ + +/** +* @brief Max qos id. +*/ +#define MQTT_MAX_QOS_ID ( 0x24 ) + +/** +* @brief Retain available id. +*/ +#define MQTT_RETAIN_AVAILABLE_ID ( 0x25 ) + +/** +* @brief Assigned client identifier id. +*/ +#define MQTT_ASSIGNED_CLIENT_ID ( 0x12 ) + +/** +* @brief Reason string id. +*/ +#define MQTT_REASON_STRING_ID ( 0x1F ) + +/** +* @brief Wildcard available id. +*/ +#define MQTT_WILDCARD_ID ( 0x28 ) + +/** +* @brief Subscription available id. +*/ +#define MQTT_SUB_AVAILABLE_ID ( 0x29 ) + +/** +* @brief Shared subscription id. +*/ +#define MQTT_SHARED_SUB_ID ( 0x2A ) + +/** +* @brief Server keep alive id. +*/ +#define MQTT_SERVER_KEEP_ALIVE_ID ( 0x13 ) + +/** +* @brief Response information id. +*/ + +#define MQTT_RESPONSE_INFO_ID ( 0x1A ) + +/** +* @brief Server reference id. +*/ +#define MQTT_SERVER_REF_ID ( 0x1C ) + +/** +* @brief Subscription ID id +*/ +#define MQTT_SUBSCRIPTION_ID_ID ( 0x0B ) + + /** * @ingroup mqtt_struct_types * @brief Buffer passed to MQTT library. @@ -179,6 +457,27 @@ typedef struct MQTTConnectInfo uint16_t passwordLength; } MQTTConnectInfo_t; +/** + * @ingroup mqtt_enum_types + * @brief Retain Handling types. + */ +typedef enum MQTTRetainHandling{ + retainSendOnSub = 0, /**< Send retained messages at the time of subscription. */ + retainSendOnSubIfNotPresent = 1, /**< Send retained messages at subscription only if subscription does not currently exist. */ + retainDoNotSendonSub = 2 /**< Do not send retained messages at the time of subscription. */ +}MQTTRetainHandling_t; + + +/** + * @ingroup mqtt_enum_types + * @brief MQTT Subscription packet types. + */ +typedef enum MQTTSubscriptionType +{ + MQTT_TYPE_SUBSCRIBE, /**< @brief The type is a SUBSCRIBE packet. */ + MQTT_TYPE_UNSUBSCRIBE /**< @brief The type is a UNSUBSCRIBE packet. */ +} MQTTSubscriptionType_t; + /** * @ingroup mqtt_struct_types * @brief MQTT SUBSCRIBE packet parameters. @@ -186,7 +485,7 @@ typedef struct MQTTConnectInfo typedef struct MQTTSubscribeInfo { /** - * @brief Quality of Service for subscription. + * @brief Quality of Service for subscription. Include protocol error of qos > 2 */ MQTTQoS_t qos; @@ -196,11 +495,169 @@ typedef struct MQTTSubscribeInfo const char * pTopicFilter; /** - * @brief Length of subscription topic filter. + * @brief Length of subscription topic filter - unsigned long */ uint16_t topicFilterLength; + /** + * @brief no local option for subscription. Include protocol error if noLocalOption = 1 in a shared subscription + */ + + /** + * @brief If true, Application Messages that are published to this subscription + * will not be forwarded to the Client that published them. + */ + bool noLocalOption; + + /** + * @brief If true, Application Messages forwarded using this subscription keep the RETAIN + * flag they were published with. + */ + bool retainAsPublishedOption; + + /** + * @brief Specifies whether retained messages are sent + * when the subscription is established. + */ + MQTTRetainHandling_t retainHandlingOption; + } MQTTSubscribeInfo_t; + +/** +* @ingroup mqtt_struct_types +* @brief Struct to hold user property. +*/ +typedef struct MQTTUserProperty +{ + /** + * @brief key. + */ + const char* pKey; + /** + * @brief Length of the key. + */ + uint16_t keyLength; + /** + * @brief value. + */ + const char* pValue; + /** + * @brief Length of the value. + */ + uint16_t valueLength; +} MQTTUserProperty_t; + +/** +* @ingroup mqtt_struct_types +* @brief Struct to hold connect and connack properties. +*/ +typedef struct MQTTConnectProperties +{ + /** + * @brief Four Byte Integer representing the Session Expiry Interval in seconds. + */ + uint32_t sessionExpiry; + + /** + * @brief Maximum number of unacknowledged PUBLISH packets client is willing to receive. + */ + uint16_t receiveMax; + + /** + * @brief Four Byte Integer representing the Maximum Packet Size the Client is willing to accept. + */ + uint32_t maxPacketSize; + + /** + * @brief Two Byte Integer representing the Topic Alias Maximum value. + */ + uint16_t topicAliasMax; + + /** + * @brief A value of 0 indicates that the Server MUST NOT return Response Information. + */ + bool requestResponseInfo; + + /** + * @brief The Client uses this value to indicate whether the Reason String or User Properties + * are sent in the case of failures + */ + bool requestProblemInfo; + + /** + * @brief Maximum number of unacknowledged PUBLISH packets server is willing to receive. + */ + uint16_t serverReceiveMax; + + /** + * @brief Max qos supported by the server. + */ + uint8_t serverMaxQos; + + /** + * @brief Byte declares whether the Server supports retained messages. + */ + uint8_t retainAvailable; + + /** + * @brief Four Byte Integer representing the Maximum Packet Size the Server is willing to accept. + */ + uint32_t serverMaxPacketSize; + + /** + * @brief Two Byte Integer representing the Topic Alias Maximum value. + */ + uint16_t serverTopicAliasMax; + + /** + * @brief Whether wildcard subscription is available. + */ + uint8_t isWildcardAvailable; + + /** + * @brief Whether the Server supports Subscription Identifiers. + */ + uint8_t isSubscriptionIdAvailable; + + /** + * @brief Whether the Server supports Shared Subscription. + */ + uint8_t isSharedAvailable; + + /** + * @brief Keep Alive value given by the server. + */ + uint16_t serverKeepAlive; + + +} MQTTConnectProperties_t; + + /** + * @ingroup mqtt_struct_types + * @brief Struct to hold reason codes. + */ +typedef struct MQTTReasonCodeInfo +{ + /** @brief Pointer to the reason code array. */ + const uint8_t * reasonCode; + + /** @brief Length of the reason code array. */ + size_t reasonCodeLength; + +} MQTTReasonCodeInfo_t; + +/** + * @ingroup mqtt_struct_types + * @brief Property builder for MQTT packets. + */ +typedef struct MQTTPropBuilder +{ + uint8_t * pBuffer; /**< @brief Pointer to the buffer for storing properties. */ + size_t bufferLength; /**< @brief Total length of the buffer available for properties. */ + size_t currentIndex; /**< @brief Current position in the buffer where next property will be written. */ + uint32_t fieldSet; /**< @brief Bitfield tracking which properties have been added. */ +} MQTTPropBuilder_t; + /** * @ingroup mqtt_struct_types * @brief MQTT PUBLISH packet parameters. @@ -241,6 +698,12 @@ typedef struct MQTTPublishInfo * @brief Message payload length. */ size_t payloadLength; + + /** + * @brief Length of the properties. + */ + size_t propertyLength; + } MQTTPublishInfo_t; /** @@ -270,132 +733,23 @@ typedef struct MQTTPacketInfo size_t headerLength; } MQTTPacketInfo_t; -/** - * @brief Get the size and Remaining Length of an MQTT CONNECT packet. - * - * This function must be called before #MQTT_SerializeConnect in order to get - * the size of the MQTT CONNECT packet that is generated from #MQTTConnectInfo_t - * and optional #MQTTPublishInfo_t. The size of the #MQTTFixedBuffer_t supplied - * to #MQTT_SerializeConnect must be at least @p pPacketSize. The provided - * @p pConnectInfo and @p pWillInfo are valid for serialization with - * #MQTT_SerializeConnect only if this function returns #MQTTSuccess. The - * remaining length returned in @p pRemainingLength and the packet size returned - * in @p pPacketSize are valid only if this function returns #MQTTSuccess. - * - * @param[in] pConnectInfo MQTT CONNECT packet parameters. - * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used. - * @param[out] pRemainingLength The Remaining Length of the MQTT CONNECT packet. - * @param[out] pPacketSize The total size of the MQTT CONNECT packet. - * - * @return #MQTTBadParameter if the packet would exceed the size allowed by the - * MQTT spec; #MQTTSuccess otherwise. - * - * Example - * @code{c} - * - * // Variables used in this example. - * MQTTStatus_t status; - * MQTTConnectInfo_t connectInfo = { 0 }; - * MQTTPublishInfo_t willInfo = { 0 }; - * size_t remainingLength = 0, packetSize = 0; - * - * // Initialize the connection info, the details are out of scope for this example. - * initializeConnectInfo( &connectInfo ); - * - * // Initialize the optional will info, the details are out of scope for this example. - * initializeWillInfo( &willInfo ); - * - * // Get the size requirement for the connect packet. - * status = MQTT_GetConnectPacketSize( - * &connectInfo, &willInfo, &remainingLength, &packetSize - * ); - * - * if( status == MQTTSuccess ) - * { - * // The application should allocate or use a static #MQTTFixedBuffer_t - * // of size >= packetSize to serialize the connect request. - * } - * @endcode - */ -/* @[declare_mqtt_getconnectpacketsize] */ -MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, - const MQTTPublishInfo_t * pWillInfo, - size_t * pRemainingLength, - size_t * pPacketSize ); -/* @[declare_mqtt_getconnectpacketsize] */ - -/** - * @brief Serialize an MQTT CONNECT packet in the given fixed buffer @p pFixedBuffer. - * - * #MQTT_GetConnectPacketSize should be called with @p pConnectInfo and - * @p pWillInfo before invoking this function to get the size of the required - * #MQTTFixedBuffer_t and @p remainingLength. The @p remainingLength must be - * the same as returned by #MQTT_GetConnectPacketSize. The #MQTTFixedBuffer_t - * must be at least as large as the size returned by #MQTT_GetConnectPacketSize. - * - * @param[in] pConnectInfo MQTT CONNECT packet parameters. - * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used. - * @param[in] remainingLength Remaining Length provided by #MQTT_GetConnectPacketSize. - * @param[out] pFixedBuffer Buffer for packet serialization. - * - * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet; - * #MQTTBadParameter if invalid parameters are passed; - * #MQTTSuccess otherwise. - * - * Example - * @code{c} - * - * // Variables used in this example. - * MQTTStatus_t status; - * MQTTConnectInfo_t connectInfo = { 0 }; - * MQTTPublishInfo_t willInfo = { 0 }; - * MQTTFixedBuffer_t fixedBuffer; - * uint8_t buffer[ BUFFER_SIZE ]; - * size_t remainingLength = 0, packetSize = 0; - * - * fixedBuffer.pBuffer = buffer; - * fixedBuffer.size = BUFFER_SIZE; - * - * // Assume connectInfo and willInfo are initialized. Get the size requirement for - * // the connect packet. - * status = MQTT_GetConnectPacketSize( - * &connectInfo, &willInfo, &remainingLength, &packetSize - * ); - * assert( status == MQTTSuccess ); - * assert( packetSize <= BUFFER_SIZE ); - * - * // Serialize the connect packet into the fixed buffer. - * status = MQTT_SerializeConnect( &connectInfo, &willInfo, remainingLength, &fixedBuffer ); - * - * if( status == MQTTSuccess ) - * { - * // The connect packet can now be sent to the broker. - * } - * @endcode - */ -/* @[declare_mqtt_serializeconnect] */ -MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, - const MQTTPublishInfo_t * pWillInfo, - size_t remainingLength, - const MQTTFixedBuffer_t * pFixedBuffer ); -/* @[declare_mqtt_serializeconnect] */ /** * @brief Get packet size and Remaining Length of an MQTT SUBSCRIBE packet. * - * This function must be called before #MQTT_SerializeSubscribe in order to get + * This function must be called before #sendSubscribeWithoutCopy in order to get * the size of the MQTT SUBSCRIBE packet that is generated from the list of - * #MQTTSubscribeInfo_t. The size of the #MQTTFixedBuffer_t supplied - * to #MQTT_SerializeSubscribe must be at least @p pPacketSize. The provided - * @p pSubscriptionList is valid for serialization with #MQTT_SerializeSubscribe - * only if this function returns #MQTTSuccess. The remaining length returned in - * @p pRemainingLength and the packet size returned in @p pPacketSize are valid + * #MQTTSubscribeInfo_t and optional subscribe properties. + * The remaining length returned in @p pRemainingLength and + * the packet size returned in @p pPacketSize are valid * only if this function returns #MQTTSuccess. * * @param[in] pSubscriptionList List of MQTT subscription info. * @param[in] subscriptionCount The number of elements in pSubscriptionList. + * @param[in] pSubscribeProperties MQTT SUBSCRIBE properties builder. Pass NULL if not used. * @param[out] pRemainingLength The Remaining Length of the MQTT SUBSCRIBE packet. * @param[out] pPacketSize The total size of the MQTT SUBSCRIBE packet. + * @param[in] maxPacketSize Maximum packet size. * * @return #MQTTBadParameter if the packet would exceed the size allowed by the * MQTT spec; #MQTTSuccess otherwise. @@ -406,6 +760,7 @@ MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, * // Variables used in this example. * MQTTStatus_t status; * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 }; + * MQTTPropBuilder_t subscribeProperties = { 0 }; * size_t remainingLength = 0, packetSize = 0; * // This is assumed to be a list of filters we want to subscribe to. * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ]; @@ -417,25 +772,37 @@ MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, * // Each subscription needs a topic filter. * subscriptionList[ i ].pTopicFilter = filters[ i ]; * subscriptionList[ i ].topicFilterLength = strlen( filters[ i ] ); + * subscriptionList[ i ].noLocalOption = false; + * subscriptionList[ i ].retainAsPublishedOption = false; + * subscriptionList[ i ].retainHandlingOption = retainSendOnSub; * } * + * // Initialize subscribe properties (if needed) + * initializeSubscribeProperties( &subscribeProperties ); + * * // Get the size requirement for the subscribe packet. * status = MQTT_GetSubscribePacketSize( - * &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize + * &subscriptionList[ 0 ], + * NUMBER_OF_SUBSCRIPTIONS, + * &subscribeProperties, + * &remainingLength, + * &packetSize, + * maxPacketSize * ); * * if( status == MQTTSuccess ) * { - * // The application should allocate or use a static #MQTTFixedBuffer_t - * // of size >= packetSize to serialize the subscribe request. + * // The subscribe packet can now be sent to the broker. * } * @endcode */ /* @[declare_mqtt_getsubscribepacketsize] */ MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, - size_t subscriptionCount, - size_t * pRemainingLength, - size_t * pPacketSize ); + size_t subscriptionCount, + const MQTTPropBuilder_t * pSubscribeProperties, + size_t * pRemainingLength, + size_t * pPacketSize, + uint32_t maxPacketSize ); /* @[declare_mqtt_getsubscribepacketsize] */ /** @@ -449,6 +816,8 @@ MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscript * * @param[in] pSubscriptionList List of MQTT subscription info. * @param[in] subscriptionCount The number of elements in pSubscriptionList. + * @param[in] pSubscribeProperties MQTT v5.0 properties for the SUBSCRIBE packet. Can be NULL + * if no properties are needed. * @param[in] packetId packet ID generated by #MQTT_GetPacketId. * @param[in] remainingLength Remaining Length provided by #MQTT_GetSubscribePacketSize. * @param[out] pFixedBuffer Buffer for packet serialization. @@ -463,6 +832,7 @@ MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscript * // Variables used in this example. * MQTTStatus_t status; * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 }; + * MQTTPropBuilder_t subscribeProperties = { 0 }; * MQTTFixedBuffer_t fixedBuffer; * uint8_t buffer[ BUFFER_SIZE ]; * size_t remainingLength = 0, packetSize = 0; @@ -475,9 +845,10 @@ MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscript * // scope for this example. * packetId = getNewPacketId(); * - * // Assume subscriptionList has been initialized. Get the subscribe packet size. + * // Assume subscriptionList and subscribeProperties have been initialized. Get the subscribe packet size. * status = MQTT_GetSubscribePacketSize( - * &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize + * &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &subscribeProperties, + * &remainingLength, &packetSize * ); * assert( status == MQTTSuccess ); * assert( packetSize <= BUFFER_SIZE ); @@ -486,6 +857,7 @@ MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscript * status = MQTT_SerializeSubscribe( * &subscriptionList[ 0 ], * NUMBER_OF_SUBSCRIPTIONS, + * &subscribeProperties, * packetId, * remainingLength, * &fixedBuffer @@ -499,28 +871,28 @@ MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscript */ /* @[declare_mqtt_serializesubscribe] */ MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, - size_t subscriptionCount, - uint16_t packetId, - size_t remainingLength, - const MQTTFixedBuffer_t * pFixedBuffer ); + size_t subscriptionCount, + const MQTTPropBuilder_t * pSubscribeProperties, + uint16_t packetId, + size_t remainingLength, + const MQTTFixedBuffer_t * pFixedBuffer ); /* @[declare_mqtt_serializesubscribe] */ /** * @brief Get packet size and Remaining Length of an MQTT UNSUBSCRIBE packet. * - * This function must be called before #MQTT_SerializeUnsubscribe in order to + * This function must be called before #sendSubscribeWithoutCopy in order to * get the size of the MQTT UNSUBSCRIBE packet that is generated from the list - * of #MQTTSubscribeInfo_t. The size of the #MQTTFixedBuffer_t supplied - * to #MQTT_SerializeUnsubscribe must be at least @p pPacketSize. The provided - * @p pSubscriptionList is valid for serialization with #MQTT_SerializeUnsubscribe - * only if this function returns #MQTTSuccess. The remaining length returned in + * of #MQTTSubscribeInfo_t and optional propertyLength. The remaining length returned in * @p pRemainingLength and the packet size returned in @p pPacketSize are valid * only if this function returns #MQTTSuccess. * * @param[in] pSubscriptionList List of MQTT subscription info. * @param[in] subscriptionCount The number of elements in pSubscriptionList. + * @param[in] pUnsubscribeProperties MQTT UNSUBSCRIBE properties builder. Pass NULL if not used. * @param[out] pRemainingLength The Remaining Length of the MQTT UNSUBSCRIBE packet. * @param[out] pPacketSize The total size of the MQTT UNSUBSCRIBE packet. + * @param[in] maxPacketSize Maximum packet size. * * @return #MQTTBadParameter if the packet would exceed the size allowed by the * MQTT spec; #MQTTSuccess otherwise. @@ -532,147 +904,119 @@ MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionL * MQTTStatus_t status; * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 }; * size_t remainingLength = 0, packetSize = 0; + * MQTTPropBuilder_t unsubscribeProperties = { 0 }; + * size_t maxPacketSize = 0; + * + * // Initialize maxPacketSize. The details are out of scope for this example. + * initializeMaxPacketSize( &maxPacketSize ); * * // Initialize the subscribe info. The details are out of scope for this example. * initializeSubscribeInfo( &subscriptionList[ 0 ] ); * + * //Initialize the property buffer. The details are out of scope for this example. + * initializePropertyBuffer( &unsubscribeProperties ); + * * // Get the size requirement for the unsubscribe packet. * status = MQTT_GetUnsubscribePacketSize( - * &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize - * ); + * &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &unsubscribeProperties, &remainingLength, &packetSize, maxPacketSize); * * if( status == MQTTSuccess ) * { - * // The application should allocate or use a static #MQTTFixedBuffer_t - * // of size >= packetSize to serialize the unsubscribe request. + * // The unsubscribe packet can now be sent to the broker. * } * @endcode */ /* @[declare_mqtt_getunsubscribepacketsize] */ -MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList, +MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t* pSubscriptionList, size_t subscriptionCount, - size_t * pRemainingLength, - size_t * pPacketSize ); + const MQTTPropBuilder_t * pUnsubscribeProperties, + size_t* pRemainingLength, + size_t* pPacketSize, + uint32_t maxPacketSize); /* @[declare_mqtt_getunsubscribepacketsize] */ /** - * @brief Serialize an MQTT UNSUBSCRIBE packet in the given buffer. + * @brief Serialize an MQTT UNSUBSCRIBE packet with properties in the given buffer. * * #MQTT_GetUnsubscribePacketSize should be called with @p pSubscriptionList - * before invoking this function to get the size of the required + * and @p pUnsubscribeProperties before invoking this function to get the size of the required * #MQTTFixedBuffer_t and @p remainingLength. The @p remainingLength must be * the same as returned by #MQTT_GetUnsubscribePacketSize. The #MQTTFixedBuffer_t * must be at least as large as the size returned by #MQTT_GetUnsubscribePacketSize. * - * @param[in] pSubscriptionList List of MQTT subscription info. + * @param[in] pSubscriptionList List of MQTT subscription info to unsubscribe from. * @param[in] subscriptionCount The number of elements in pSubscriptionList. - * @param[in] packetId packet ID generated by #MQTT_GetPacketId. + * @param[in] pUnsubscribeProperties MQTT 5.0 properties for the UNSUBSCRIBE packet. Can be NULL if no properties are needed. + * @param[in] packetId Packet identifier used for the UNSUBSCRIBE packet. * @param[in] remainingLength Remaining Length provided by #MQTT_GetUnsubscribePacketSize. - * @param[out] pFixedBuffer Buffer for packet serialization. + * @param[out] pFixedBuffer Buffer where the serialized UNSUBSCRIBE packet will be written. * * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet; - * #MQTTBadParameter if invalid parameters are passed; - * #MQTTSuccess otherwise. + * #MQTTBadParameter if any of the parameters are invalid (NULL pSubscriptionList or pFixedBuffer, zero subscriptionCount); + * #MQTTSuccess if the packet was serialized successfully. * * Example * @code{c} * * // Variables used in this example. * MQTTStatus_t status; - * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 }; + * MQTTSubscribeInfo_t subscriptionList[2]; + * MQTTPropBuilder_t unsubscribeProperties; * MQTTFixedBuffer_t fixedBuffer; - * uint8_t buffer[ BUFFER_SIZE ]; + * uint8_t buffer[100]; * size_t remainingLength = 0, packetSize = 0; - * uint16_t packetId; + * uint16_t packetId = 1; * + * // Initialize the fixed buffer. * fixedBuffer.pBuffer = buffer; - * fixedBuffer.size = BUFFER_SIZE; + * fixedBuffer.size = sizeof( buffer ); * - * // Function to return a valid, unused packet identifier. The details are out of - * // scope for this example. - * packetId = getNewPacketId(); + * // Initialize subscription list. + * subscriptionList[0].pTopicFilter = "topic/1"; + * subscriptionList[0].topicFilterLength = strlen("topic/1"); + * subscriptionList[1].pTopicFilter = "topic/2"; + * subscriptionList[1].topicFilterLength = strlen("topic/2"); * - * // Assume subscriptionList has been initialized. Get the unsubscribe packet size. - * status = MQTT_GetUnsubscribePacketSize( - * &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize - * ); - * assert( status == MQTTSuccess ); - * assert( packetSize <= BUFFER_SIZE ); + * // Initialize properties (optional) * - * // Serialize the unsubscribe packet into the fixed buffer. - * status = MQTT_SerializeUnsubscribe( - * &subscriptionList[ 0 ], - * NUMBER_OF_SUBSCRIPTIONS, - * packetId, - * remainingLength, - * &fixedBuffer + * // Get size requirement for the unsubscribe packet. + * status = MQTT_GetUnsubscribePacketSize( + * subscriptionList, + * 2, + * &unsubscribeProperties, + * &remainingLength, + * &packetSize * ); * * if( status == MQTTSuccess ) * { - * // The unsubscribe packet can now be sent to the broker. + * // Serialize unsubscribe packet. + * status = MQTT_SerializeUnsubscribe( + * subscriptionList, + * 2, + * &unsubscribeProperties, + * packetId, + * remainingLength, + * &fixedBuffer + * ); * } - * @endcode - */ -/* @[declare_mqtt_serializeunsubscribe] */ -MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, - size_t subscriptionCount, - uint16_t packetId, - size_t remainingLength, - const MQTTFixedBuffer_t * pFixedBuffer ); -/* @[declare_mqtt_serializeunsubscribe] */ - -/** - * @brief Get the packet size and remaining length of an MQTT PUBLISH packet. - * - * This function must be called before #MQTT_SerializePublish in order to get - * the size of the MQTT PUBLISH packet that is generated from #MQTTPublishInfo_t. - * The size of the #MQTTFixedBuffer_t supplied to #MQTT_SerializePublish must be - * at least @p pPacketSize. The provided @p pPublishInfo is valid for - * serialization with #MQTT_SerializePublish only if this function returns - * #MQTTSuccess. The remaining length returned in @p pRemainingLength and the - * packet size returned in @p pPacketSize are valid only if this function - * returns #MQTTSuccess. - * - * @param[in] pPublishInfo MQTT PUBLISH packet parameters. - * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet. - * @param[out] pPacketSize The total size of the MQTT PUBLISH packet. - * - * @return #MQTTBadParameter if the packet would exceed the size allowed by the - * MQTT spec or if invalid parameters are passed; #MQTTSuccess otherwise. - * - * Example - * @code{c} - * - * // Variables used in this example. - * MQTTStatus_t status; - * MQTTPublishInfo_t publishInfo = { 0 }; - * size_t remainingLength = 0, packetSize = 0; - * - * // Initialize the publish info. - * publishInfo.qos = MQTTQoS0; - * publishInfo.pTopicName = "/some/topic/name"; - * publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); - * publishInfo.pPayload = "Hello World!"; - * publishInfo.payloadLength = strlen( "Hello World!" ); - * - * // Get the size requirement for the publish packet. - * status = MQTT_GetPublishPacketSize( - * &publishInfo, &remainingLength, &packetSize - * ); * * if( status == MQTTSuccess ) * { - * // The application should allocate or use a static #MQTTFixedBuffer_t - * // of size >= packetSize to serialize the publish. + * // The unsubscribe packet has been serialized successfully. + * // The serialized packet is now ready to be sent to the broker. * } + * * @endcode */ -/* @[declare_mqtt_getpublishpacketsize] */ -MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, - size_t * pRemainingLength, - size_t * pPacketSize ); -/* @[declare_mqtt_getpublishpacketsize] */ +/* @[declare_mqtt_serializeunsubscribe] */ +MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList, + size_t subscriptionCount, + const MQTTPropBuilder_t * pUnsubscribeProperties, + uint16_t packetId, + size_t remainingLength, + const MQTTFixedBuffer_t * pFixedBuffer ); +/* @[declare_mqtt_serializeunsubscribe] */ /** * @brief Serialize an MQTT PUBLISH packet in the given buffer. @@ -689,6 +1033,8 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, * as the size returned by #MQTT_GetPublishPacketSize. * * @param[in] pPublishInfo MQTT PUBLISH packet parameters. + * @param[in] pPublishProperties MQTT v5.0 properties for the PUBLISH packet. Can be NULL + * if no properties are needed. * @param[in] packetId packet ID generated by #MQTT_GetPacketId. * @param[in] remainingLength Remaining Length provided by #MQTT_GetPublishPacketSize. * @param[out] pFixedBuffer Buffer for packet serialization. @@ -703,6 +1049,7 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, * // Variables used in this example. * MQTTStatus_t status; * MQTTPublishInfo_t publishInfo = { 0 }; + * MQTTPropBuilder_t publishProperties = { 0 }; * MQTTFixedBuffer_t fixedBuffer; * uint8_t buffer[ BUFFER_SIZE ]; * size_t remainingLength = 0, packetSize = 0; @@ -715,9 +1062,9 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, * // identifier must be used. * packetId = 0; * - * // Assume publishInfo has been initialized. Get publish packet size. + * // Assume publishInfo and publishProperties have been initialized. Get publish packet size. * status = MQTT_GetPublishPacketSize( - * &publishInfo, &remainingLength, &packetSize + * &publishInfo, &publishProperties, &remainingLength, &packetSize * ); * assert( status == MQTTSuccess ); * assert( packetSize <= BUFFER_SIZE ); @@ -725,6 +1072,7 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, * // Serialize the publish packet into the fixed buffer. * status = MQTT_SerializePublish( * &publishInfo, + * &publishProperties, * packetId, * remainingLength, * &fixedBuffer @@ -738,6 +1086,7 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, */ /* @[declare_mqtt_serializepublish] */ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, + const MQTTPropBuilder_t * pPublishProperties, uint16_t packetId, size_t remainingLength, const MQTTFixedBuffer_t * pFixedBuffer ); @@ -756,10 +1105,12 @@ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo, * * @return #MQTTSuccess if the serialization is successful. Otherwise, #MQTTBadParameter. */ +/* @[declare_mqtt_serializepublishheaderwithouttopic] */ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * pPublishInfo, size_t remainingLength, uint8_t * pBuffer, size_t * headerSize ); +/* @[declare_mqtt_serializepublishheaderwithouttopic] */ /** * @brief Serialize an MQTT PUBLISH packet header in the given buffer. @@ -777,6 +1128,8 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * * as the size returned by #MQTT_GetPublishPacketSize. * * @param[in] pPublishInfo MQTT PUBLISH packet parameters. + * @param[in] pPublishProperties MQTT v5.0 properties for the PUBLISH packet. Can be NULL + * if no properties are needed. * @param[in] packetId packet ID generated by #MQTT_GetPacketId. * @param[in] remainingLength Remaining Length provided by #MQTT_GetPublishPacketSize. * @param[out] pFixedBuffer Buffer for packet serialization. @@ -792,11 +1145,13 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * * // Variables used in this example. * MQTTStatus_t status; * MQTTPublishInfo_t publishInfo = { 0 }; + * MQTTPropBuilder_t publishProperties ; * MQTTFixedBuffer_t fixedBuffer; * uint8_t buffer[ BUFFER_SIZE ]; * size_t remainingLength = 0, packetSize = 0, headerSize = 0; * uint16_t packetId; * int32_t bytesSent; + * uint32_t maxPacketSize = pContext->connectProperties.serverMaxPacketSize; * * fixedBuffer.pBuffer = buffer; * fixedBuffer.size = BUFFER_SIZE; @@ -805,10 +1160,10 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * * // identifier must be used. * packetId = 0; * - * // Assume publishInfo has been initialized. Get the publish packet size. + * // Assume publishInfo and publishProperties have been initialized. Get the publish packet size. * status = MQTT_GetPublishPacketSize( - * &publishInfo, &remainingLength, &packetSize - * ); + * &publishInfo, &publishProperties, &remainingLength, &packetSize, maxPacketSize +); * assert( status == MQTTSuccess ); * // The payload will not be serialized, so the the fixed buffer does not need to hold it. * assert( ( packetSize - publishInfo.payloadLength ) <= BUFFER_SIZE ); @@ -816,6 +1171,7 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * * // Serialize the publish packet header into the fixed buffer. * status = MQTT_SerializePublishHeader( * &publishInfo, + * &publishProperties, * packetId, * remainingLength, * &fixedBuffer, @@ -836,10 +1192,11 @@ MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * */ /* @[declare_mqtt_serializepublishheader] */ MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo, - uint16_t packetId, - size_t remainingLength, - const MQTTFixedBuffer_t * pFixedBuffer, - size_t * pHeaderSize ); + const MQTTPropBuilder_t * pPublishProperties, + uint16_t packetId, + size_t remainingLength, + const MQTTFixedBuffer_t * pFixedBuffer, + size_t * pHeaderSize ); /* @[declare_mqtt_serializepublishheader] */ /** @@ -889,74 +1246,7 @@ MQTTStatus_t MQTT_SerializeAck( const MQTTFixedBuffer_t * pFixedBuffer, uint16_t packetId ); /* @[declare_mqtt_serializeack] */ -/** - * @brief Get the size of an MQTT DISCONNECT packet. - * - * @param[out] pPacketSize The size of the MQTT DISCONNECT packet. - * - * @return #MQTTSuccess, or #MQTTBadParameter if @p pPacketSize is NULL. - * - * Example - * @code{c} - * - * // Variables used in this example. - * MQTTStatus_t status; - * size_t packetSize = 0; - * - * // Get the size requirement for the disconnect packet. - * status = MQTT_GetDisconnectPacketSize( &packetSize ); - * assert( status == MQTTSuccess ); - * assert( packetSize == 2 ); - * - * // The application should allocate or use a static #MQTTFixedBuffer_t of - * // size >= 2 to serialize the disconnect packet. - * - * @endcode - */ -/* @[declare_mqtt_getdisconnectpacketsize] */ -MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize ); -/* @[declare_mqtt_getdisconnectpacketsize] */ -/** - * @brief Serialize an MQTT DISCONNECT packet into the given buffer. - * - * The input #MQTTFixedBuffer_t.size must be at least as large as the size - * returned by #MQTT_GetDisconnectPacketSize. - * - * @param[out] pFixedBuffer Buffer for packet serialization. - * - * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet; - * #MQTTBadParameter if invalid parameters are passed; - * #MQTTSuccess otherwise. - * - * Example - * @code{c} - * - * // Variables used in this example. - * MQTTStatus_t status; - * MQTTFixedBuffer_t fixedBuffer; - * uint8_t buffer[ BUFFER_SIZE ]; - * - * fixedBuffer.pBuffer = buffer; - * fixedBuffer.size = BUFFER_SIZE; - * - * // Get the disconnect packet size. - * status = MQTT_GetDisconnectPacketSize( &packetSize ); - * assert( status == MQTTSuccess ); - * assert( packetSize <= BUFFER_SIZE ); - * - * // Serialize the disconnect into the fixed buffer. - * status = MQTT_SerializeDisconnect( &fixedBuffer ); - * - * if( status == MQTTSuccess ) - * { - * // The disconnect packet can now be sent to the broker. - * } - * @endcode - */ -/* @[declare_mqtt_serializedisconnect] */ -MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer ); -/* @[declare_mqtt_serializedisconnect] */ /** * @brief Get the size of an MQTT PINGREQ packet. @@ -1033,6 +1323,9 @@ MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer ); * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer. * @param[out] pPacketId The packet ID obtained from the buffer. * @param[out] pPublishInfo Struct containing information about the publish. + * @param[in] propBuffer Buffer to hold the properties. + * @param[in] maxPacketSize Maximum packet size. + * @param[in] topicAliasMax Maximum topic alias specified in the CONNECT packet. * * @return #MQTTBadParameter, #MQTTBadResponse, or #MQTTSuccess. * @@ -1052,7 +1345,10 @@ MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer ); * MQTTStatus_t status; * MQTTPacketInfo_t incomingPacket; * MQTTPublishInfo_t publishInfo = { 0 }; + * MQTTPropBuilder_t propBuffer ; + * uint32_t maxPacketSize; * uint16_t packetId; + * uint16_t topicAliasMax ; * * int32_t bytesRecvd; * // A buffer to hold remaining data of the incoming packet. @@ -1076,7 +1372,7 @@ MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer ); * // Deserialize the publish information if the incoming packet is a publish. * if( ( incomingPacket.type & 0xF0 ) == MQTT_PACKET_TYPE_PUBLISH ) * { - * status = MQTT_DeserializePublish( &incomingPacket, &packetId, &publishInfo ); + * status = MQTT_DeserializePublish( &incomingPacket, &packetId, &publishInfo, &propBuffer, maxPacketSize, topicAliasMax ); * if( status == MQTTSuccess ) * { * // The deserialized publish information can now be used from `publishInfo`. @@ -1085,55 +1381,14 @@ MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer ); * @endcode */ /* @[declare_mqtt_deserializepublish] */ -MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket, - uint16_t * pPacketId, - MQTTPublishInfo_t * pPublishInfo ); +MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t* pIncomingPacket, + uint16_t* pPacketId, + MQTTPublishInfo_t* pPublishInfo, + MQTTPropBuilder_t* propBuffer, + uint32_t maxPacketSize, + uint16_t topicAliasMax ); /* @[declare_mqtt_deserializepublish] */ -/** - * @brief Deserialize an MQTT CONNACK, SUBACK, UNSUBACK, PUBACK, PUBREC, PUBREL, - * PUBCOMP, or PINGRESP. - * - * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer. - * @param[out] pPacketId The packet ID of obtained from the buffer. Not used - * in CONNACK or PINGRESP. - * @param[out] pSessionPresent Boolean flag from a CONNACK indicating present session. - * - * @return #MQTTBadParameter, #MQTTBadResponse, #MQTTServerRefused, or #MQTTSuccess. - * - * Example - * @code{c} - * - * // Variables used in this example. - * MQTTStatus_t status; - * MQTTPacketInfo_t incomingPacket; - * // Used for SUBACK, UNSUBACK, PUBACK, PUBREC, PUBREL, and PUBCOMP. - * uint16_t packetId; - * // Used for CONNACK. - * bool sessionPresent; - * - * // Receive an incoming packet and populate all fields. The details are out of scope - * // for this example. - * receiveIncomingPacket( &incomingPacket ); - * - * // Deserialize ack information if the incoming packet is not a publish. - * if( ( incomingPacket.type & 0xF0 ) != MQTT_PACKET_TYPE_PUBLISH ) - * { - * status = MQTT_DeserializeAck( &incomingPacket, &packetId, &sessionPresent ); - * if( status == MQTTSuccess ) - * { - * // The packet ID or session present flag information is available. For - * // ping response packets, the only information is the status code. - * } - * } - * @endcode - */ -/* @[declare_mqtt_deserializeack] */ -MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket, - uint16_t * pPacketId, - bool * pSessionPresent ); -/* @[declare_mqtt_deserializeack] */ - /** * @brief Extract the MQTT packet type and length from incoming packet. * @@ -1305,6 +1560,1496 @@ uint8_t * MQTT_SerializeUnsubscribeHeader( size_t remainingLength, uint16_t packetId ); /** @endcond */ +/** + * @brief Get the size and Remaining Length of an MQTT Version 5 CONNECT packet. + * + * This function must be called before #sendConnectWithoutCopy in order to get + * the size of the MQTT CONNECT packet that is generated from #MQTTConnectInfo_t, + * optional #MQTTPublishInfo_t and optional connect and will properties. + * + * @param[in] pConnectInfo MQTT CONNECT packet parameters. + * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used. + * @param[in] pConnectProperties MQTT CONNECT properties builder. Pass NULL if not used. + * @param[in] pWillProperties MQTT Will properties builder. Pass NULL if not used. + * @param[out] pRemainingLength The Remaining Length of the MQTT CONNECT packet. + * @param[out] pPacketSize The total size of the MQTT CONNECT packet. + * + * @return #MQTTBadParameter if the packet would exceed the size allowed by the + * MQTT spec; #MQTTSuccess otherwise. + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTConnectInfo_t connectInfo = { 0 }; + * MQTTPublishInfo_t willInfo = { 0 }; + * MQTTPropBuilder_t connectProperties = { 0 }; + * MQTTPropBuilder_t willProperties = { 0 }; + * size_t remainingLength = 0, packetSize = 0; + * + * // Initialize the connection info, the details are out of scope for this example. + * initializeConnectInfo( &connectInfo ); + * + * // Initialize the optional will info, the details are out of scope for this example. + * initializeWillInfo( &willInfo ); + * + * // Initialize connect properties and will properties, the details are out of scope for this example. + * initializeConnectProperties( &connectProperties ); + * initializeWillProperties( &willProperties ); + * + * // Get the size requirement for the connect packet. + * status = MQTT_GetConnectPacketSize( + * &connectInfo, + * &willInfo, + * &connectProperties, + * &willProperties, + * &remainingLength, + * &packetSize + * ); + * + * if( status == MQTTSuccess ) + * { + * // The application should allocate or use a static #MQTTFixedBuffer_t + * // of size >= packetSize to serialize the connect request. + * } + * @endcode + */ +/* @[declare_mqtt_getconnectpacketsize] */ +MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo, + const MQTTPublishInfo_t * pWillInfo, + const MQTTPropBuilder_t *pConnectProperties, + const MQTTPropBuilder_t *pWillProperties, + size_t * pRemainingLength, + size_t * pPacketSize ); +/* @[declare_mqtt_getconnectpacketsize] */ + +/** + * @brief Serialize an MQTT CONNECT packet in the given fixed buffer @p pFixedBuffer. + * + * #MQTT_GetConnectPacketSize should be called with @p pConnectInfo, @p pWillInfo, + * @p pConnectProperties, and @p pWillProperties before invoking this function to get + * the size of the required #MQTTFixedBuffer_t and @p remainingLength. The + * @p remainingLength must be the same as returned by #MQTT_GetConnectPacketSize. + * The #MQTTFixedBuffer_t must be at least as large as the size returned by + * #MQTT_GetConnectPacketSize. + * + * @param[in] pConnectInfo MQTT CONNECT packet parameters. + * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used. + * @param[in] pConnectProperties MQTT CONNECT properties builder. Pass NULL if not used. + * @param[in] pWillProperties MQTT Will properties builder. Pass NULL if not used. + * @param[in] remainingLength Remaining Length provided by #MQTT_GetConnectPacketSize. + * @param[out] pFixedBuffer Buffer for packet serialization. + * + * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet; + * #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise. + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTConnectInfo_t connectInfo = { 0 }; + * MQTTPublishInfo_t willInfo = { 0 }; + * MQTTPropBuilder_t connectProperties = { 0 }; + * MQTTPropBuilder_t willProperties = { 0 }; + * MQTTFixedBuffer_t fixedBuffer; + * uint8_t buffer[ BUFFER_SIZE ]; + * size_t remainingLength = 0, packetSize = 0; + * + * fixedBuffer.pBuffer = buffer; + * fixedBuffer.size = BUFFER_SIZE; + * + * // Assume connectInfo, willInfo, and properties are initialized. + * // Get the size requirement for the connect packet. + * status = MQTT_GetConnectPacketSize( + * &connectInfo, &willInfo, &connectProperties, &willProperties, + * &remainingLength, &packetSize + * ); + * assert( status == MQTTSuccess ); + * assert( packetSize <= BUFFER_SIZE ); + * + * // Serialize the connect packet into the fixed buffer. + * status = MQTT_SerializeConnect( + * &connectInfo, + * &willInfo, + * &connectProperties, + * &willProperties, + * remainingLength, + * &fixedBuffer + * ); + * + * if( status == MQTTSuccess ) + * { + * // The connect packet can now be sent to the broker. + * } + * @endcode + */ +/* @[declare_mqtt_serializeconnect] */ +MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo, + const MQTTPublishInfo_t * pWillInfo, + const MQTTPropBuilder_t * pConnectProperties, + const MQTTPropBuilder_t * pWillProperties, + size_t remainingLength, + const MQTTFixedBuffer_t * pFixedBuffer ); +/* @[declare_mqtt_serializeconnect] */ + +/** + * @brief Validate the publish parameters present in the given publish structure @p pPublishInfo. + * + * This function must be called before #MQTT_GetPublishPacketSize in order to validate the publish parameters. + * + * @param[in] pPublishInfo MQTT publish packet parameters. + * @param[in] retainAvailable Whether server allows retain or not. + * @param[in] maxQos Maximum QoS supported by the server. + * @param[in] topicAlias Topic alias in the PUBLISH packet. + * @param[in] maxPacketSize Maximum packet size allowed by the server. + * + * @return #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise. + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTPublishInfo_t publishInfo = {0}; + * uint16_t topicAlias; + * uint8_t retainAvailable; + * uint8_t maxQos; + * // Set in the CONNACK packet. + * uint32_t maxPacketSize ; + * + * //Set the publish info parameters. + * + * //Validate the publish packet + * status = MQTT_ValidatePublishParams(&publishInfo, retainAvailable, maxQos, topicAlias, maxPacketSize); + * + * if( status == MQTTSuccess ) + * { + * // Get the packet size and serialize the publish packet. + * } + * @endcode + */ +/* @[declare_mqtt_validatepublishparams] */ +MQTTStatus_t MQTT_ValidatePublishParams(const MQTTPublishInfo_t* pPublishInfo, + uint8_t retainAvailable, + uint8_t maxQos, + uint16_t topicAlias, + uint32_t maxPacketSize); +/* @[declare_mqtt_validatepublishparams] */ + + +/** + * @brief Get the packet size and remaining length of an MQTT PUBLISH packet. + * + * #MQTT_ValidatePublishParams should be called with @p pPublishInfo before invoking this function + * to validate the publish parameters. This function must be called before #sendPublishWithoutCopy + * in order to get the size of the MQTT PUBLISH packet that is generated from #MQTTPublishInfo_t + * and optional publish properties. The remaining length returned in @p pRemainingLength and the + * packet size returned in @p pPacketSize are valid only if this function + * returns #MQTTSuccess. + * + * @param[in] pPublishInfo MQTT PUBLISH packet parameters. + * @param[in] pPublishProperties MQTT PUBLISH properties builder. Pass NULL if not used. + * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet. + * @param[out] pPacketSize The total size of the MQTT PUBLISH packet. + * @param[in] maxPacketSize Maximum packet size allowed by the server. + * + * @return #MQTTBadParameter if the packet would exceed the size allowed by the + * MQTT spec or if invalid parameters are passed; #MQTTSuccess otherwise. + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTPublishInfo_t publishInfo = { 0 }; + * MQTTPropBuilder_t publishProperties = { 0 }; + * uint16_t topicAliasMax; + * uint8_t retainAvailable; + * uint8_t maxQos; + * size_t remainingLength = 0, packetSize = 0; + * + * // Initialize the publish info. + * publishInfo.qos = MQTTQoS0; + * publishInfo.pTopicName = "/some/topic/name"; + * publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); + * publishInfo.pPayload = "Hello World!"; + * publishInfo.payloadLength = strlen( "Hello World!" ); + * + * // Initialize publish properties (if needed) + * initializePublishProperties( &publishProperties ); + * + * // Validate publish parameters + * status = MQTT_ValidatePublishParams(&publishInfo, topicAliasMax, retainAvailable, maxQos); + * + * // Get the size requirement for the publish packet. + * status = MQTT_GetPublishPacketSize( + * &publishInfo, + * &publishProperties, + * &remainingLength, + * &packetSize, + * maxPacketSize + * ); + * + * if( status == MQTTSuccess ) + * { + * // The publish packet can now be sent to the broker. + * } + * @endcode + */ +/* @[declare_mqtt_getpublishpacketsize] */ +MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo, + const MQTTPropBuilder_t * pPublishProperties, + size_t * pRemainingLength, + size_t * pPacketSize, + uint32_t maxPacketSize); +/* @[declare_mqtt_getpublishpacketsize] */ + + +/** + * @brief Deserialize an MQTT PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, PINGRESP or CONNACK. + * + * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer. + * @param[out] pPacketId The packet ID obtained from the buffer. + * @param[out] pSessionPresent Boolean flag indicating if a session is present (only valid for CONNACK packets). + * For CONNACK: true if session is present, false otherwise. + * For other packet types: this parameter is ignored. + * @param[out] pReasonCode Struct to store reason code(s) from the acknowledgment packet. + * Contains the success/failure status of the corresponding request. + * @param[in] requestProblem Request problem value set in the connect packet. + * Indicates if there was an issue with the original request. + * @param[in] maxPacketSize Maximum packet size allowed by the client. + * Used for validation of incoming packet size. + * @param[out] propBuffer Struct to store the deserialized acknowledgment properties. + * Will contain any MQTT v5.0 properties included in the ack packet. + * @param[in,out] pConnectProperties Struct to store the deserialized connect/connack properties. + * Used only for CONNACK packets to store connection-specific properties. + * For other packet types, this parameter can be NULL. + * + * @return Returns one of the following: + * - #MQTTSuccess if the packet was successfully deserialized + * - #MQTTBadParameter if invalid parameters are passed + * - #MQTTBadResponse if the packet is malformed + * - #MQTTServerRefused if the server explicitly rejected the request + * - #MQTTBadResponse if the packet type is invalid or packet parsing fails + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTPacketInfo_t incomingPacket; + * uint16_t packetId; + * bool sessionPresent; + * MQTTReasonCodeInfo_t reasonCode ; // Can be set to NULL if the incoming packet is CONNACK or PINGRESP + * bool requestProblem = = pContext->connectProperties.requestProblemInfo ; // only relevant if the incoming packet is a PUBLISH Ack. + * uint32_t maxPacketSize = pContext->connectProperties.maxPacketSize ; + * MQTTPropBuilder_t propBuffer; // Can be set to NULL if the user does not want any incoming properties. + * MQTTConnectProperties_t connectProperties = pContext->connectProperties; // Can be set to NULL if the incoming packet is PUBLISH ACKs, SUBACK, UNSUBACK or PINGRESP + * + * // Receive an incoming packet and populate all fields. The details are out of scope + * // for this example. + * receiveIncomingPacket(&incomingPacket); + * + * // Deserialize ack information if the incoming packet is a publish ack. + * status = MQTT_DeserializeAck(&incomingPacket, + * &packetId, + * &sessionPresent, + * &reasonCode, + * requestProblem, + * maxPacketSize, + * &propBuffer, + * &connectProperties); + * if(status == MQTTSuccess) + * {s + * // Ack information is now available. + * } + * @endcode + */ +/* @[declare_mqtt_deserializeack] */ +MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket, + uint16_t * pPacketId, + bool * pSessionPresent, + MQTTReasonCodeInfo_t * pReasonCode, + bool requestProblem, + uint32_t maxPacketSize, + MQTTPropBuilder_t * propBuffer, + MQTTConnectProperties_t * pConnectProperties ); +/* @[declare_mqtt_deserializeack] */ + +/** + * @fn uint8_t* MQTT_SerializeAckFixed(uint8_t* pIndex, + uint8_t packetType, + uint16_t packetId, + size_t remainingLength, + MQTTSuccessFailReasonCode_t reasonCode); + * @brief Serialize the fixed size part of the ack packet header. + * + * @param[out] pIndex Pointer to the buffer where the header is to + * be serialized. + * @param[in] packetType Type of publish ack + * @param[in] packetId Packed identifier of the ack packet. + * @param[in] remainingLength Remaining length of the ack packet. + * @param[in] reasonCode Reason code for the ack packet. + * + * @return A pointer to the end of the encoded string. + */ + +/** + * @cond DOXYGEN_IGNORE + * Doxygen should ignore this definition, this function is private. + */ +uint8_t* MQTT_SerializeAckFixed(uint8_t* pIndex, + uint8_t packetType, + uint16_t packetId, + size_t remainingLength, + MQTTSuccessFailReasonCode_t reasonCode); +/** @endcond */ + +/** + * @brief Serialize an MQTT PUBLISH ACK packet into the given buffer. + * + * The input #MQTTFixedBuffer_t.size must be at least as large as the size + * returned by #MQTT_GetAckPacketSize. + * + * @note If reason code is success and property length is zero then #MQTT_SerializeAck can also be used. + * + * @param[out] pRemainingLength The remaining length of the packet to be serialized. + * @param[out] pPacketSize The size of the packet to be serialized. + * @param[in] maxPacketSize Maximum packet size allowed by the server. + * @param[in] ackPropertyLength The length of the properties. + * + * @return #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise. + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTFixedBuffer_t fixedBuffer; + * uint8_t buffer[ BUFFER_SIZE ]; + * MQTTAckInfo_t ackInfo; + * uint16_t sessionExpiry; + * + * fixedBuffer.pBuffer = buffer; + * fixedBuffer.size = BUFFER_SIZE; + * // Variables used in this example. + * MQTTStatus_t status; + * size_t remainingLength =0; + * size_t packetSize = 0; + * size_t ackPropertyLength = 0; + * uint32_t maxPacketSize; + * //set the parameters. + * // Get the size requirement for the ack packet. + * status = MQTT_GetAckPacketSize(&remainingLength,&packetSize,maxPacketSize, ackPropertyLength); + * } + * @endcode + */ +/* @[declare_mqtt_getackpacketsize] */ +MQTTStatus_t MQTT_GetAckPacketSize(size_t* pRemainingLength, + size_t* pPacketSize, + uint32_t maxPacketSize, + size_t ackPropertyLength); +/* @[declare_mqtt_getackpacketsize] */ + +/** + * @brief Get the size of an MQTT DISCONNECT packet. + * + * @param[in] pDisconnectProperties MQTT DISCONNECT properties builder. Pass NULL if not used. + * @param[out] pRemainingLength The Remaining Length of the MQTT DISCONNECT packet. + * @param[out] pPacketSize The size of the MQTT DISCONNECT packet. + * @param[in] maxPacketSize Maximum packet size allowed by the server. + * @param[in] reasonCode The reason code for the disconnect. + * + * @return #MQTTSuccess, or #MQTTBadParameter if parameters are invalid + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * size_t remainingLength = 0; + * size_t packetSize = 0; + * uint32_t maxPacketSize; + * MQTTPropBuilder_t disconnectProperties ; + * MQTTSuccessFailReasonCode_t reasonCode; + * + * //Set property builder. The details are out of scope for this example. + * initializePropertyBuilder( &disconnectProperties ); + * + * //Set the parameters. + * // Get the size requirement for the disconnect packet. + * status = MQTT_GetDisconnectPacketSize(&disconnectProperties, &remainingLength,&packetSize,maxPacketSize, reasonCode); + * + * if( status == MQTTSuccess ) + * { + * // Send the disconnect packet. + * } + * @endcode + */ +/* @[declare_mqtt_getdisconnectpacketsize] */ +MQTTStatus_t MQTT_GetDisconnectPacketSize( const MQTTPropBuilder_t * pDisconnectProperties, + size_t* pRemainingLength, + size_t* pPacketSize, + uint32_t maxPacketSize, + MQTTSuccessFailReasonCode_t reasonCode); +/* @[declare_mqtt_getdisconnectpacketsize] */ + +/** + * @brief Serialize an MQTT DISCONNECT packet into the given buffer. + * + * The input #MQTTFixedBuffer_t.size must be at least as large as the size + * returned by #MQTT_GetDisconnectPacketSize. This function should only be called + * after #MQTT_GetDisconnectPacketSize to ensure proper buffer sizing. + * + * @param[in] pDisconnectProperties MQTT v5.0 properties for the DISCONNECT packet. Can be NULL + * if no properties are needed. + * @param[in] reasonCode The reason code for the disconnect. For MQTT v5.0, this indicates + * why the connection is being terminated. + * @param[in] remainingLength Remaining Length provided by #MQTT_GetDisconnectPacketSize. + * @param[out] pFixedBuffer Buffer for packet serialization. + * + * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet; + * #MQTTBadParameter if invalid parameters are passed; + * #MQTTSuccess otherwise. + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTFixedBuffer_t fixedBuffer; + * MQTTPropBuilder_t disconnectProperties = { 0 }; + * uint8_t buffer[ BUFFER_SIZE ]; + * size_t remainingLength = 0, packetSize = 0; + * + * fixedBuffer.pBuffer = buffer; + * fixedBuffer.size = BUFFER_SIZE; + * + * // Get the disconnect packet size. + * status = MQTT_GetDisconnectPacketSize( &disconnectProperties, + * MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, + * &remainingLength, + * &packetSize ); + * assert( status == MQTTSuccess ); + * assert( packetSize <= BUFFER_SIZE ); + * + * // Serialize the disconnect into the fixed buffer. + * status = MQTT_SerializeDisconnect( &disconnectProperties, + * MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, + * remainingLength, + * &fixedBuffer ); + * + * if( status == MQTTSuccess ) + * { + * // The disconnect packet can now be sent to the broker. + * } + * @endcode + */ +/* @[declare_mqtt_serializedisconnect] */ +MQTTStatus_t MQTT_SerializeDisconnect( const MQTTPropBuilder_t *pDisconnectProperties, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength, + const MQTTFixedBuffer_t * pFixedBuffer ); +/* @[declare_mqtt_serializedisconnect] */ + +/** + * @fn uint8_t * MQTT_SerializeDisconnectFixed(uint8_t * pIndex, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength); + * @brief Serialize the fixed part of the disconnect packet header. + * + * @param[out] pIndex Pointer to the buffer where the fixed size parameters is to be serialized. + * @param[in] reasonCode The disconnect reason code. + * @param[in] remainingLength The remaining length of the packet to be serialized. + * @return A pointer to the end of the encoded string. + */ + +/** + * @cond DOXYGEN_IGNORE + * Doxygen should ignore this definition, this function is private. + */ +uint8_t * MQTT_SerializeDisconnectFixed(uint8_t * pIndex, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength); +/** @endcond */ + +/** + * @brief Deserialize an MQTT Disconnect packet. + * + * @param[in] pPacket #MQTTPacketInfo_t containing the buffer. + * @param[in] maxPacketSize Maximum packet size allowed by the client. + * @param[out] pDisconnectInfo Struct containing disconnect reason code + * @param[out] propBuffer MQTTPropBuilder_t to store the deserialized properties. + * + * @return #MQTTBadParameter, #MQTTServerRefused, #MQTTBadResponse or #MQTTSuccess. + * + * Example + * @code{c} + * + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTPacketInfo_t incomingPacket; + * MQTTReasonCodeInfo_t disconnectInfo; + * uint32_t maxPacketSize; + * MQTTPropBuilder_t propBuffer; // Assume this is initialized properly + * // Receive an incoming packet and populate all fields. The details are out of scope + * // for this example. + * receiveIncomingPacket( &incomingPacket ); + * + * // Deserialize disconnect information. + * if( ( incomingPacket.type) == MQTT_PACKET_TYPE_DISCONNECT ) + * { + * status = MQTT_DeserializeDisconnect(&incomingPacket, +* maxPacketSize, + &disconnectInfo, + &propBuffer); + * if( status == MQTTSuccess ) + * { + * // Disconnect information is available. + * } + * } + * @endcode + */ +/* @[declare_mqtt_deserializedisconnect] */ +MQTTStatus_t MQTT_DeserializeDisconnect(const MQTTPacketInfo_t* pPacket, + uint32_t maxPacketSize, + MQTTReasonCodeInfo_t* pDisconnectInfo, + MQTTPropBuilder_t* propBuffer); +/* @[declare_mqtt_deserializedisconnect] */ + + +/** + * @brief Updates the MQTT context with connect properties from the property builder. + * + * This function processes the property builder and updates the connect properties + * in the MQTT context. It handles the conversion and validation of properties from + * the property builder to the connect properties structure. + * + * @param[in] pPropBuilder Pointer to the property builder containing MQTT properties. + * Must not be NULL. + * @param[out] pConnectProperties Pointer to the connection properties structure to be updated. + * Must not be NULL. + * + * @return Returns one of the following: + * - #MQTTSuccess if properties were successfully updated + * - #MQTTBadParameter, MQTTBadResponse if invalid parameters are passed + * + * Example + * @code{c} + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTPropBuilder_t propBuilder = { 0 }; + * MQTTConnectProperties_t connectProperties = { 0 }; + * + * // Initialize property builder with desired properties + * // ... + * + * // Update connect properties + * status = updateContextWithConnectProps(&propBuilder, &connectProperties); + * + * if(status == MQTTSuccess) + * { + * // Properties successfully updated in the context + * } + * @endcode + */ + +MQTTStatus_t updateContextWithConnectProps(const MQTTPropBuilder_t* pPropBuilder, MQTTConnectProperties_t* pConnectProperties); + + +/** + * @brief Adds a Subscription Identifier property to the MQTT property builder. + * + * This function adds a Subscription Identifier property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure where + * the Subscription Identifier will be added. + * Must not be NULL. + * @param[in] subscriptionId The Subscription Identifier value to be added. + * Must be greater than 0. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Subscription Identifier was successfully added + * - #MQTTBadParameter if pPropertyBuilder is NULL or subscriptionId is 0 + * - #MQTTNoMemory if the property builder has insufficient space + * + * Example + * @code{c} + * // Variables used in this example. + * MQTTStatus_t status; + * MQTTPropBuilder_t propertyBuilder ; // Assume this is initialized properly + * size_t subscriptionId = 12345; + * + * // Add Subscription Identifier to property builder + * status = MQTTPropAdd_SubscribeId(&propertyBuilder, subscriptionId); + * + * if(status == MQTTSuccess) + * { + * // Subscription Identifier successfully added + * } + * @endcode + * + * @note This property is only valid for MQTT v5.0 and above. + * @note The Subscription Identifier can be used in SUBSCRIBE packets and + * will be returned in matched PUBLISH packets. + */ + +/* @[declare_mqttpropadd_subscribeid] */ +MQTTStatus_t MQTTPropAdd_SubscribeId(MQTTPropBuilder_t* pPropertyBuilder, size_t subscriptionId); +/* @[declare_mqttpropadd_subscribeid] */ + +/** + * @brief Adds User Property to the MQTT property builder. + * + * This function adds User Property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] userProperty The User Property to be added. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Subscription Identifier was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_userprop] */ +MQTTStatus_t MQTTPropAdd_UserProp(MQTTPropBuilder_t* pPropertyBuilder, const MQTTUserProperty_t* userProperty); +/* @[declare_mqttpropadd_userprop] */ + +/** + * @brief Adds Session Expiry Interval property to the MQTT property builder. + * + * This function adds Session Expiry Interval property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] sessionExpiry The Session Expiry Interval in seconds. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Session Expiry Interval was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_sessionexpiry] */ +MQTTStatus_t MQTTPropAdd_SessionExpiry(MQTTPropBuilder_t* pPropertyBuilder, uint32_t sessionExpiry); +/* @[declare_mqttpropadd_sessionexpiry] */ + +/** + * @brief Adds Receive Maximum property to the MQTT property builder. + * + * This function adds Receive Maximum property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] receiveMax The maximum number of QoS 1 and QoS 2 messages allowed to be received simultaneously. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Receive Maximum was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_connreceivemax] */ +MQTTStatus_t MQTTPropAdd_ConnReceiveMax(MQTTPropBuilder_t* pPropertyBuilder, uint16_t receiveMax); +/* @[declare_mqttpropadd_connreceivemax] */ + +/** + * @brief Adds Maximum Packet Size property to the MQTT property builder. + * + * This function adds Maximum Packet Size property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] maxPacketSize The maximum packet size the client is willing to accept. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Maximum Packet Size was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_connmaxpacketsize] */ +MQTTStatus_t MQTTPropAdd_ConnMaxPacketSize(MQTTPropBuilder_t* pPropertyBuilder, uint32_t maxPacketSize); +/* @[declare_mqttpropadd_connmaxpacketsize] */ + +/** + * @brief Adds Topic Alias Maximum property to the MQTT property builder. + * + * This function adds Topic Alias Maximum property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] topicAliasMax The maximum value of topic alias accepted by the client. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Topic Alias Maximum was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_conntopicaliasmax] */ +MQTTStatus_t MQTTPropAdd_ConnTopicAliasMax(MQTTPropBuilder_t* pPropertyBuilder, uint16_t topicAliasMax); +/* @[declare_mqttpropadd_conntopicaliasmax] */ + +/** + * @brief Adds Request Response Information property to the MQTT property builder. + * + * This function adds Request Response Information property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] requestResponseInfo Boolean indicating whether response information is requested. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Request Response Information was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_connrequestrespinfo] */ +MQTTStatus_t MQTTPropAdd_ConnRequestRespInfo(MQTTPropBuilder_t* pPropertyBuilder, bool requestResponseInfo); +/* @[declare_mqttpropadd_connrequestrespinfo] */ + +/** + * @brief Adds Request Problem Information property to the MQTT property builder. + * + * This function adds Request Problem Information property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] requestProblemInfo Boolean indicating whether problem information is requested. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Request Problem Information was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_connrequestprobinfo] */ +MQTTStatus_t MQTTPropAdd_ConnRequestProbInfo(MQTTPropBuilder_t* pPropertyBuilder, bool requestProblemInfo); +/* @[declare_mqttpropadd_connrequestprobinfo] */ + +/** + * @brief Adds Authentication Method property to the MQTT property builder. + * + * This function adds Authentication Method property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] authMethod Pointer to the authentication method string. + * @param[in] authMethodLength Length of the authentication method string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Authentication Method was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_connauthmethod] */ +MQTTStatus_t MQTTPropAdd_ConnAuthMethod(MQTTPropBuilder_t* pPropertyBuilder, + const char* authMethod, + uint16_t authMethodLength); +/* @[declare_mqttpropadd_connauthmethod] */ + +/** + * @brief Adds Authentication Data property to the MQTT property builder. + * + * This function adds Authentication Data property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] authData Pointer to the authentication data. + * @param[in] authDataLength Length of the authentication data. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Authentication Data was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_connauthdata] */ +MQTTStatus_t MQTTPropAdd_ConnAuthData( MQTTPropBuilder_t* pPropertyBuilder, + const char* authData, + uint16_t authDataLength); +/* @[declare_mqttpropadd_connauthdata] */ + +/** + * @brief Adds Payload Format Indicator property to the MQTT property builder. + * + * This function adds Payload Format Indicator property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] payloadFormat Boolean indicating the payload format (true for UTF-8, false for unspecified bytes). + * + * @return Returns one of the following: + * - #MQTTSuccess if the Payload Format Indicator was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_pubpayloadformat] */ +MQTTStatus_t MQTTPropAdd_PubPayloadFormat(MQTTPropBuilder_t* pPropertyBuilder, bool payloadFormat); +/* @[declare_mqttpropadd_pubpayloadformat] */ + +/** + * @brief Adds Message Expiry Interval property to the MQTT property builder. + * + * This function adds Message Expiry Interval property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] messageExpiry The message expiry interval in seconds. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Message Expiry Interval was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_pubmessageexpiry] */ +MQTTStatus_t MQTTPropAdd_PubMessageExpiry(MQTTPropBuilder_t* pPropertyBuilder, uint32_t messageExpiry); +/* @[declare_mqttpropadd_pubmessageexpiry] */ + +/** + * @brief Adds Will Delay Interval property to the MQTT property builder. + * + * This function adds Message Expiry Interval property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] willDelayInterval Will Delay Interval in seconds. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Message Expiry Interval was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_willdelayinterval] */ +MQTTStatus_t MQTTPropAdd_WillDelayInterval( MQTTPropBuilder_t * pPropertyBuilder, + uint32_t willDelayInterval ); +/* @[declare_mqttpropadd_willdelayinterval] */ + +/** + * @brief Adds Topic Alias property to the MQTT property builder. + * + * This function adds Topic Alias property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] topicAlias The topic alias value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Topic Alias was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_pubtopicalias] */ +MQTTStatus_t MQTTPropAdd_PubTopicAlias(MQTTPropBuilder_t* pPropertyBuilder, uint16_t topicAlias); +/* @[declare_mqttpropadd_pubtopicalias] */ + +/** + * @brief Adds Response Topic property to the MQTT property builder. + * + * This function adds Response Topic property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] responseTopic Pointer to the response topic string. + * @param[in] responseTopicLength Length of the response topic string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Response Topic was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_pubresponsetopic] */ +MQTTStatus_t MQTTPropAdd_PubResponseTopic( MQTTPropBuilder_t* pPropertyBuilder, + const char* responseTopic, + uint16_t responseTopicLength); +/* @[declare_mqttpropadd_pubresponsetopic] */ +/** + * @brief Adds Correlation Data property to the MQTT property builder. + * + * This function adds Correlation Data property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] pCorrelationData Pointer to the correlation data. + * @param[in] correlationLength Length of the correlation data. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Correlation Data was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_pubcorrelationdata] */ +MQTTStatus_t MQTTPropAdd_PubCorrelationData(MQTTPropBuilder_t* pPropertyBuilder, + const void* pCorrelationData, + uint16_t correlationLength); +/* @[declare_mqttpropadd_pubcorrelationdata] */ + +/** + * @brief Adds Content Type property to the MQTT property builder. + * + * This function adds Content Type property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] contentType Pointer to the content type string. + * @param[in] contentTypeLength Length of the content type string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Content Type was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_pubcontenttype] */ +MQTTStatus_t MQTTPropAdd_PubContentType(MQTTPropBuilder_t* pPropertyBuilder, + const char* contentType, + uint16_t contentTypeLength); +/* @[declare_mqttpropadd_pubcontenttype] */ + +/** + * @brief Adds Reason String property to the MQTT property builder. + * + * This function adds Reason String property to the property builder. + * + * @param[out] pPropertyBuilder Pointer to the property builder structure. + * @param[in] pReasonString Pointer to the reason string. + * @param[in] reasonStringLength Length of the reason string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Reason String was successfully added + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTNoMemory if the property builder has insufficient space + */ +/* @[declare_mqttpropadd_reasonstring] */ +MQTTStatus_t MQTTPropAdd_ReasonString(MQTTPropBuilder_t* pPropertyBuilder, + const char* pReasonString, + uint16_t reasonStringLength); +/* @[declare_mqttpropadd_reasonstring] */ + +/** + * @brief Validates the properties of a PUBLISH packet. + * + * This function validates the properties in the property builder for a PUBLISH packet. + * + * @param[in] serverTopicAliasMax Maximum topic alias value allowed by the server. + * @param[in] propBuilder Pointer to the property builder structure. + * @param[out] topicAlias Pointer to store the topic alias value if present. + * + * @return Returns one of the following: + * - #MQTTSuccess if the properties are valid + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqtt_validatepublishproperties] */ +MQTTStatus_t MQTT_ValidatePublishProperties(uint16_t serverTopicAliasMax, const MQTTPropBuilder_t* propBuilder, uint16_t *topicAlias); +/* @[declare_mqtt_validatepublishproperties] */ + +/** + * @brief Validates the properties of a SUBSCRIBE packet. + * + * This function validates the properties in the property builder for a SUBSCRIBE packet. + * + * @param[in] isSubscriptionIdAvailable Boolean indicating if subscription identifiers are supported. + * @param[in] propBuilder Pointer to the property builder structure. + * + * @return Returns one of the following: + * - #MQTTSuccess if the properties are valid + * - #MQTTBadParameter if an invalid parameter is passed + */ +/* @[declare_mqtt_validatesubscribeproperties] */ +MQTTStatus_t MQTT_ValidateSubscribeProperties(uint8_t isSubscriptionIdAvailable, const MQTTPropBuilder_t* propBuilder); +/* @[declare_mqtt_validatesubscribeproperties] */ + +/** + * @brief Validates the properties specified for an MQTT DISCONNECT packet. + * + * @param[in] connectSessionExpiry The session expiry interval that was specified + * in the CONNECT packet. Used to validate that the + * DISCONNECT session expiry is not non-zero while + * connectSessionExpiry is zero. + * @param[in] pPropertyBuilder Pointer to the property builder structure containing subscribe properties. + * + * @return Returns one of the following: + * - #MQTTSuccess , #MQTTBadParameter or #MQTTBadResponse. + */ +/* @[declare_mqtt_validatedisconnectproperties] */ +MQTTStatus_t MQTT_ValidateDisconnectProperties( uint32_t connectSessionExpiry, const MQTTPropBuilder_t * pPropertyBuilder); +/* @[declare_mqtt_validatedisconnectproperties] */ + +/** + * @brief Validates the properties specified for an WILL Properties in the MQTT CONNECT packet. + * + * @param[in] pPropertyBuilder Pointer to the property builder structure containing will properties. + * + * @return Returns one of the following: + * - #MQTTSuccess , #MQTTBadParameter or #MQTTBadResponse. + */ +/* @[declare_mqtt_validatewillproperties] */ +MQTTStatus_t MQTT_ValidateWillProperties( const MQTTPropBuilder_t * pPropertyBuilder); +/* @[declare_mqtt_validatewillproperties] */ + +/** + * @brief Validates the properties specified for an MQTT UNSUBSCRIBE packet. + * + * @param[in] pPropertyBuilder Pointer to the property builder structure containing unsubscribe properties. + * + * @return Returns one of the following: + * - #MQTTSuccess , #MQTTBadParameter or #MQTTBadResponse. + */ +/* @[declare_mqtt_validateunsubscribeproperties] */ +MQTTStatus_t MQTT_ValidateUnsubscribeProperties( const MQTTPropBuilder_t * pPropertyBuilder); +/* @[declare_mqtt_validateunsubscribeproperties] */ + +/** + * @brief Validates the properties specified for an MQTT PUBLISH ACK packet. + * + * @param[in] pPropertyBuilder Pointer to the property builder structure containing unsubscribe properties. + * + * @return Returns one of the following: + * - #MQTTSuccess , #MQTTBadParameter or #MQTTBadResponse. + */ +/* @[declare_mqtt_validatepublishackproperties] */ +MQTTStatus_t MQTT_ValidatePublishAckProperties( const MQTTPropBuilder_t * pPropertyBuilder ); +/* @[declare_mqtt_validatepublishackproperties] */ + +/** + * @brief Gets the Topic Alias property from the MQTT property builder. + * + * This function retrieves the Topic Alias property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] topicAlias Pointer to store the retrieved topic alias value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Topic Alias was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_pubtopicalias] */ +MQTTStatus_t MQTTPropGet_PubTopicAlias(MQTTPropBuilder_t* propBuffer, uint16_t* topicAlias); +/* @[declare_mqttpropget_pubtopicalias] */ + +/** + * @brief Gets the Payload Format Indicator property from the MQTT property builder. + * + * This function retrieves the Payload Format Indicator property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] payloadFormat Pointer to store the retrieved Payload Format Indicator value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Topic Alias was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_pubpayloadformat] */ +MQTTStatus_t MQTTPropGet_PubPayloadFormatIndicator(MQTTPropBuilder_t* propBuffer, uint8_t* payloadFormat); +/* @[declare_mqttpropget_pubpayloadformat] */ + +/** + * @brief Gets the Response Topic property from the MQTT property builder. + * + * This function retrieves the Response Topic property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] responseTopic Pointer to store the response topic string. + * @param[out] responseTopicLength Pointer to store the length of the response topic string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Response Topic was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_pubresponsetopic] */ +MQTTStatus_t MQTTPropGet_PubResponseTopic(MQTTPropBuilder_t* propBuffer, const char** responseTopic, uint16_t* responseTopicLength); +/* @[declare_mqttpropget_pubresponsetopic] */ + +/** + * @brief Gets the Correlation Data property from the MQTT property builder. + * + * This function retrieves the Correlation Data property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] correlationData Pointer to store the correlation data. + * @param[out] correlationLength Pointer to store the length of the correlation data. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Correlation Data was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_pubcorrelationdata] */ +MQTTStatus_t MQTTPropGet_PubCorrelationData(MQTTPropBuilder_t* propBuffer, const void** correlationData, uint16_t* correlationLength); +/* @[declare_mqttpropget_pubcorrelationdata] */ + +/** + * @brief Gets the Message Expiry Interval property from the MQTT property builder. + * + * This function retrieves the Message Expiry Interval property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] msgExpiryInterval Pointer to store the message expiry interval value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Message Expiry Interval was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_pubmessageexpiryinterval] */ +MQTTStatus_t MQTTPropGet_PubMessageExpiryInterval(MQTTPropBuilder_t* propBuffer, uint32_t* msgExpiryInterval); +/* @[declare_mqttpropget_pubmessageexpiryinterval] */ + +/** + * @brief Gets the Content Type property from the MQTT property builder. + * + * This function retrieves the Content Type property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] pContentType Pointer to store the content type string. + * @param[out] contentTypeLength Pointer to store the length of the content type string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Content Type was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_pubcontenttype] */ +MQTTStatus_t MQTTPropGet_PubContentType(MQTTPropBuilder_t* propBuffer, const char** pContentType, uint16_t* contentTypeLength); +/* @[declare_mqttpropget_pubcontenttype] */ + +/** + * @brief Gets the Subscription Identifier property from the MQTT property builder. + * + * This function retrieves the Subscription Identifier property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] subscriptionId Pointer to store the subscription identifier value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Subscription Identifier was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_pubsubscriptionid] */ +MQTTStatus_t MQTTPropGet_PubSubscriptionId(MQTTPropBuilder_t* propBuffer, size_t* subscriptionId); +/* @[declare_mqttpropget_pubsubscriptionid] */ + +/** + * @brief Gets the User Property from the MQTT property builder. + * + * This function retrieves the User Property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] pUserPropKey Pointer to store the user property key string. + * @param[out] pUserPropKeyLen Pointer to store the length of the key string. + * @param[out] pUserPropVal Pointer to store the user property value string. + * @param[out] pUserPropValLen Pointer to store the length of the value string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the User Property was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_userprop] */ +MQTTStatus_t MQTTPropGet_UserProp(MQTTPropBuilder_t* propBuffer, + const char** pUserPropKey, + uint16_t* pUserPropKeyLen, + const char** pUserPropVal, + uint16_t* pUserPropValLen); +/* @[declare_mqttpropget_userprop] */ + +/** + * @brief Gets the Reason String property from the MQTT property builder. + * + * This function retrieves the Reason String property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] pReasonString Pointer to store the reason string. + * @param[out] reasonStringLength Pointer to store the length of the reason string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Reason String was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_reasonstring] */ +MQTTStatus_t MQTTPropGet_ReasonString(MQTTPropBuilder_t* propBuffer, const char** pReasonString, uint16_t* reasonStringLength); +/* @[declare_mqttpropget_reasonstring] */ + +/** + * @brief Gets the Server Reference property from the MQTT DISCONNECT packet properties. + * + * This function retrieves the Server Reference property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] pServerRef Pointer to store the server reference string. + * @param[out] serverRefLength Pointer to store the length of the server reference string. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Server Reference was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_disconnectserverref] */ +MQTTStatus_t MQTTPropGet_ServerRef(MQTTPropBuilder_t* propBuffer, const char** pServerRef, uint16_t* serverRefLength); +/* @[declare_mqttpropget_disconnectserverref] */ + +/** + * @brief Gets the Session Expiry Interval property from the MQTT CONNECT packet properties. + * + * This function retrieves the Session Expiry Interval property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] sessionExpiry Pointer to store the session expiry interval value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Session Expiry Interval was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_sessionexpiry] */ +MQTTStatus_t MQTTPropGet_SessionExpiry(MQTTPropBuilder_t* propBuffer, uint32_t* sessionExpiry); +/* @[declare_mqttpropget_sessionexpiry] */ + +/** + * @brief Gets the Topic Alias Maximum property from the MQTT CONNECT packet properties. + * + * This function retrieves the Topic Alias Maximum property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] topicAliasMax Pointer to store the topic alias maximum value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Topic Alias Maximum was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_conntopicaliasmax] */ +MQTTStatus_t MQTTPropGet_ConnTopicAliasMax(MQTTPropBuilder_t* propBuffer, uint16_t* topicAliasMax); +/* @[declare_mqttpropget_conntopicaliasmax] */ + +/** + * @brief Gets the Receive Maximum property from the MQTT CONNECT packet properties. + * + * This function retrieves the Receive Maximum property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] receiveMax Pointer to store the receive maximum value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Receive Maximum was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_connreceivemax] */ +MQTTStatus_t MQTTPropGet_ConnReceiveMax(MQTTPropBuilder_t* propBuffer, uint16_t* receiveMax); +/* @[declare_mqttpropget_connreceivemax] */ + +/** + * @brief Gets the Maximum QoS property from the MQTT CONNECT packet properties. + * + * This function retrieves the Maximum QoS property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] maxQos Pointer to store the maximum QoS value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Maximum QoS was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_connmaxqos] */ +MQTTStatus_t MQTTPropGet_ConnMaxQos(MQTTPropBuilder_t* propBuffer, uint8_t* maxQos); +/* @[declare_mqttpropget_connmaxqos] */ + +/** + * @brief Gets the Retain Available property from the MQTT CONNECT packet properties. + * + * This function retrieves the Retain Available property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] retainAvailable Pointer to store the retain available flag. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Retain Available was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_connretainavailable] */ +MQTTStatus_t MQTTPropGet_ConnRetainAvailable(MQTTPropBuilder_t* propBuffer, uint8_t* retainAvailable); +/* @[declare_mqttpropget_connretainavailable] */ + +/** + * @brief Gets the Maximum Packet Size property from the MQTT CONNECT packet properties. + * + * This function retrieves the Maximum Packet Size property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] maxPacketSize Pointer to store the maximum packet size value. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Maximum Packet Size was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_connmaxpacketsize] */ +MQTTStatus_t MQTTPropGet_ConnMaxPacketSize(MQTTPropBuilder_t* propBuffer, uint32_t* maxPacketSize); +/* @[declare_mqttpropget_connmaxpacketsize] */ + +/** + * @brief Gets the Client Identifier property from the MQTT CONNECT packet properties. + * + * This function retrieves the Client Identifier property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] pClientId Pointer to store the client identifier string. + * @param[out] clientIdLength Pointer to store the length of the client identifier. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Client Identifier was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_connclientid] */ +MQTTStatus_t MQTTPropGet_ConnClientId(MQTTPropBuilder_t* propBuffer, const char** pClientId, uint16_t* clientIdLength); +/* @[declare_mqttpropget_connclientid] */ + +/** + * @brief Gets the Wildcard Subscription Available property from the MQTT CONNECT packet properties. + * + * This function retrieves the Wildcard Subscription Available property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] isWildCardAvailable Pointer to store the wildcard subscription available flag. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Wildcard Subscription Available was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_connwildcard] */ +MQTTStatus_t MQTTPropGet_ConnWildcard(MQTTPropBuilder_t* propBuffer, uint8_t* isWildCardAvailable); +/* @[declare_mqttpropget_connwildcard] */ + +/** + * @brief Gets the Subscription Identifier Available property from the MQTT CONNECT packet properties. + * + * This function retrieves the Subscription Identifier Available property from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] isSubIdAvailable Pointer to store the subscription identifier available flag. + * + * @return Returns one of the following: + * - #MQTTSuccess if the Subscription Identifier Available was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTBadResponse if an invalid packet is read + */ +/* @[declare_mqttpropget_connsubid] */ +MQTTStatus_t MQTTPropGet_ConnSubId(MQTTPropBuilder_t* propBuffer, uint8_t* isSubIdAvailable); +/* @[declare_mqttpropget_connsubid] */ + + +/** + * @brief Get the Shared Subscriptions Available property from CONNACK properties + * + * @param[in] propBuffer The property buffer containing CONNACK properties + * @param[out] isSharedSubAvailable Pointer to store whether shared subscriptions are supported + * 1 if available, 0 if not available + * + * @return MQTTSuccess if property was found and retrieved successfully + * MQTTBadParameter if propBuffer or isSharedSubAvailable is NULL + * MQTTBadResponse if property value is invalid in buffer + */ +/* @[declare_mqttpropget_connsharedsubavailable] */ +MQTTStatus_t MQTTPropGet_ConnSharedSubAvailable( MQTTPropBuilder_t * propBuffer, + uint8_t * isSharedSubAvailable ); +/* @[declare_mqttpropget_connsharedsubavailable] */ +/** + * @brief Get the Server Keep Alive property from CONNACK properties + * + * @param[in] propBuffer The property buffer containing CONNACK properties + * @param[out] serverKeepAlive Pointer to store the server-specified keep alive interval in seconds + * + * @return MQTTSuccess if property was found and retrieved successfully + * MQTTBadParameter if propBuffer or serverKeepAlive is NULL + * MQTTBadResponse if property value is invalid in buffer + */ +/* @[declare_mqttpropget_connserverkeepalive] */ +MQTTStatus_t MQTTPropGet_ConnServerKeepAlive( MQTTPropBuilder_t * propBuffer, + uint16_t * serverKeepAlive ); +/* @[declare_mqttpropget_connserverkeepalive] */ + +/** + * @brief Get the Response Information property from CONNACK properties + * + * @param[in] propBuffer The property buffer containing CONNACK properties + * @param[out] pResponseInfo Pointer to store the response information string + * @param[out] responseInfoLength Pointer to store length of response information + * + * @return MQTTSuccess if property was found and retrieved successfully + * MQTTBadParameter if propBuffer, pResponseInfo, or responseInfoLength is NULL + * MQTTBadResponse if property value is invalid in buffer + */ +/* @[declare_mqttpropget_connresponseinfo] */ +MQTTStatus_t MQTTPropGet_ConnResponseInfo( MQTTPropBuilder_t * propBuffer, + const char ** pResponseInfo, + uint16_t * responseInfoLength ); +/* @[declare_mqttpropget_connresponseinfo] */ + +/** + * @brief Get the Authentication Method property from CONNECT/CONNACK properties + * + * @param[in] propBuffer The property buffer containing CONNECT/CONNACK properties + * @param[out] pAuthMethod Pointer to store the authentication method string + * @param[out] authMethodLength Pointer to store length of authentication method + * + * @return MQTTSuccess if property was found and retrieved successfully + * MQTTBadParameter if propBuffer, pAuthMethod, or authMethodLength is NULL + * MQTTBadResponse if property value is invalid in buffer + */ + +/* @[declare_mqttpropget_connauthmethod] */ +MQTTStatus_t MQTTPropGet_ConnAuthMethod(MQTTPropBuilder_t * propBuffer, + const char ** pAuthMethod, + uint16_t * authMethodLength); +/* @[declare_mqttpropget_connauthmethod] */ + +/** + * @brief Get the Authentication Data property from CONNECT/CONNACK properties + * + * @param[in] propBuffer The property buffer containing CONNECT/CONNACK properties + * @param[out] pAuthData Pointer to store the authentication data + * @param[out] authDataLength Pointer to store length of authentication data + * + * @return MQTTSuccess if property was found and retrieved successfully + * MQTTBadParameter if propBuffer, pAuthData, or authDataLength is NULL + * MQTTBadResponse if property value is invalid in buffer + */ + +/* @[declare_mqttpropget_connauthdata] */ +MQTTStatus_t MQTTPropGet_ConnAuthData(MQTTPropBuilder_t * propBuffer, + const char ** pAuthData, + uint16_t * authDataLength); + +/* @[declare_mqttpropget_connauthdata] */ + +/** + * @brief Gets the next property identifier from the incoming MQTT packet properties. + * + * This function retrieves the next property identifier from the property builder. + * + * @param[in] propBuffer Pointer to the property builder structure. + * @param[out] propertyId Pointer to store the next property identifier. + * + * @return Returns one of the following: + * - #MQTTSuccess if the next property identifier was successfully retrieved + * - #MQTTBadParameter if an invalid parameter is passed + * - #MQTTEndOfProperties if there are no more properties to retrieve + */ +/* @[declare_mqtt_incominggetnextprop] */ +MQTTStatus_t MQTT_IncomingGetNextProp(MQTTPropBuilder_t* propBuffer, uint8_t* propertyId); +/* @[declare_mqtt_incominggetnextprop] */ + +/** + * @brief Initialize the property builder. + * + * @param[out] pPropertyBuilder Property builder to initialize. + * @param[in] buffer Buffer to store the properties. + * @param[in] length Length of the buffer. + * + * @return + * - #MQTTBadParameter if invalid parameters are passed. + * - #MQTTSuccess otherwise. + */ +/* @[declare_mqtt_propertybuilder_init] */ +MQTTStatus_t MQTT_PropertyBuilder_Init( MQTTPropBuilder_t * pPropertyBuilder, + uint8_t * buffer, + size_t length ); +/* @[declare_mqtt_propertybuilder_init] */ + /* *INDENT-OFF* */ #ifdef __cplusplus } diff --git a/source/include/core_mqtt_utils.h b/source/include/core_mqtt_utils.h new file mode 100644 index 000000000..b1b1bff37 --- /dev/null +++ b/source/include/core_mqtt_utils.h @@ -0,0 +1,129 @@ +/* + * coreMQTT + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file core_mqtt_utils.h + * @brief utility function used in other files. + */ +#ifndef CORE_MQTT_UTILS_H +#define CORE_MQTT_UTILS_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * @brief Set a bit in an 8-bit unsigned integer. + */ +#define UINT8_SET_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) | ( 0x01U << ( position ) ) ) ) + +/** + * @brief Macro for checking if a bit is set in a 1-byte unsigned int. + * + * @param[in] x The unsigned int to check. + * @param[in] position Which bit to check. + */ +#define UINT8_CHECK_BIT( x, position ) ( ( ( x ) &( 0x01U << ( position ) ) ) == ( 0x01U << ( position ) ) ) + +/** + * @brief Set a bit in an 32-bit unsigned integer. + */ +#define UINT32_SET_BIT( x, position ) \ + ( ( x ) = ( uint32_t ) ( ( x ) | ( ( uint32_t ) 0x01U << ( position ) ) ) ) + +/** + * @brief Macro for checking if a bit is set in a 4-byte unsigned int. + * + * @param[in] x The unsigned int to check. + * @param[in] position Which bit to check. + */ +#define UINT32_CHECK_BIT( x, position ) \ + ( ( ( uint32_t ) ( x ) &( ( uint32_t ) 0x01U << ( position ) ) ) == ( ( uint32_t ) 0x01U << ( position ) ) ) + +/** + * @brief Get the high byte of a 16-bit unsigned integer. + */ +#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( ( x ) >> 8 ) ) + +/** + * @brief Get the low byte of a 16-bit unsigned integer. + */ +#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( ( x ) & 0x00ffU ) ) + +/** + * @brief Macro for decoding a 2-byte unsigned int from a sequence of bytes. + * + * @param[in] ptr A uint8_t* that points to the high byte. + */ +#define UINT16_DECODE( ptr ) \ + ( uint16_t ) ( ( ( ( uint16_t ) ptr[ 0 ] ) << 8 ) | \ + ( ( uint16_t ) ptr[ 1 ] ) ) + +/** + * @brief Macro for decoding a 4-byte unsigned int from a sequence of bytes. + * + * @param[in] ptr A uint8_t* that points to the high byte. + */ +#define UINT32_DECODE( ptr ) \ + ( uint32_t ) ( ( ( ( uint32_t ) ptr[ 0 ] ) << 24 ) | \ + ( ( ( uint32_t ) ptr[ 1 ] ) << 16 ) | \ + ( ( ( uint32_t ) ptr[ 2 ] ) << 8 ) | \ + ( ( uint32_t ) ptr[ 3 ] ) ) + +/** + * @brief Macro to extract a specific byte from a 32-bit unsigned integer. + * + * @param[in] x The 32-bit unsigned integer to extract from. + * @param[in] byteNumber The byte position to extract (0-3). + */ +#define UINT32_GET_BYTE( x, byteNumber ) \ + ( ( uint8_t ) ( ( ( uint32_t ) ( x ) >> ( ( uint8_t ) ( byteNumber ) * 8U ) ) & 0xFFU ) ) + +/** + * @brief Clear a bit in an 8-bit unsigned integer. + */ +#define UINT8_CLEAR_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) &( ~( 0x01U << ( position ) ) ) ) ) + +/** + * @brief Encodes the remaining length of the packet using the variable length + * encoding scheme provided in the MQTT v3.1.1 specification. + * + * @param[out] pDestination The destination buffer to store the encoded remaining + * length. + * @param[in] length The remaining length to encode. + * + * @return The location of the byte following the encoded value. + */ +uint8_t * encodeVariableLength( uint8_t * pDestination, + size_t length ); + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* ifndef CORE_MQTT_UTILS_H*/ diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index 714c945e3..0ab671866 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -4,6 +4,7 @@ include( ${MODULE_ROOT_DIR}/mqttFilePaths.cmake ) # ==================== Define your project name (edit) ======================== set(project_name "core_mqtt") + # ===================== Create your mock here (edit) ======================== # list the files to mock here @@ -111,4 +112,4 @@ create_test(${utest_name} "${utest_link_list}" "${utest_dep_list}" "${test_include_directories}" - ) + ) \ No newline at end of file diff --git a/test/unit-test/core_mqtt_config.h b/test/unit-test/core_mqtt_config.h index 24b577f57..768eca070 100644 --- a/test/unit-test/core_mqtt_config.h +++ b/test/unit-test/core_mqtt_config.h @@ -43,7 +43,7 @@ * 3. Include the header file "logging_stack.h", if logging is enabled for MQTT. */ -#include "logging_levels.h" +#include "../logging/logging_levels.h" /* Logging configuration for the MQTT library. */ #ifndef LIBRARY_LOG_NAME @@ -54,7 +54,7 @@ #define LIBRARY_LOG_LEVEL LOG_NONE #endif -#include "logging_stack.h" +#include "../logging/logging_stack.h" /************ End of logging configuration ****************/ @@ -71,6 +71,6 @@ #define MQTT_SUB_UNSUB_MAX_VECTORS ( 6U ) -#define MQTT_SEND_TIMEOUT_MS ( 20U ) +#define MQTT_SEND_TIMEOUT_MS ( 200U ) #endif /* ifndef CORE_MQTT_CONFIG_H_ */ diff --git a/test/unit-test/core_mqtt_serializer_utest.c b/test/unit-test/core_mqtt_serializer_utest.c index c359e296c..6ffa1e0e5 100644 --- a/test/unit-test/core_mqtt_serializer_utest.c +++ b/test/unit-test/core_mqtt_serializer_utest.c @@ -28,7 +28,7 @@ */ #include #include - +#include #include "unity.h" /* Include paths for public enums, structures, and macros. */ @@ -65,48 +65,61 @@ struct NetworkContext #define TEST_TOPIC_NAME ( "/test/topic" ) /**< @brief An arbitrary topic name. */ #define TEST_TOPIC_NAME_LENGTH ( ( uint16_t ) ( sizeof( TEST_TOPIC_NAME ) - 1 ) ) /**< @brief Length of topic name. */ - /** * @brief MQTT protocol version 3.1.1. */ -#define MQTT_VERSION_3_1_1 ( ( uint8_t ) 4U ) +#define MQTT_VERSION_3_1_1 ( ( uint8_t ) 4U ) /** * @brief Test-defined macro for MQTT username. */ -#define MQTT_TEST_USERNAME "username" -#define MQTT_TEST_USERNAME_LEN ( sizeof( MQTT_TEST_USERNAME ) - 1 ) +#define MQTT_TEST_USERNAME "username" +#define MQTT_TEST_USERNAME_LEN ( sizeof( MQTT_TEST_USERNAME ) - 1 ) /** * @brief Test-defined macro for MQTT password. */ -#define MQTT_TEST_PASSWORD "password" -#define MQTT_TEST_PASSWORD_LEN ( sizeof( MQTT_TEST_PASSWORD ) - 1 ) +#define MQTT_TEST_PASSWORD "password" +#define MQTT_TEST_PASSWORD_LEN ( sizeof( MQTT_TEST_PASSWORD ) - 1 ) /** * @brief Length of the client identifier. */ -#define MQTT_CLIENT_IDENTIFIER_LEN ( sizeof( MQTT_CLIENT_IDENTIFIER ) - 1 ) +#define MQTT_CLIENT_IDENTIFIER_LEN ( sizeof( MQTT_CLIENT_IDENTIFIER ) - 1 ) /** * @brief Sample payload. */ -#define MQTT_SAMPLE_PAYLOAD "Hello World" -#define MQTT_SAMPLE_PAYLOAD_LEN ( sizeof( MQTT_SAMPLE_PAYLOAD ) - 1 ) +#define MQTT_SAMPLE_PAYLOAD "Hello World" +#define MQTT_SAMPLE_PAYLOAD_LEN ( sizeof( MQTT_SAMPLE_PAYLOAD ) - 1 ) + +#define TEST_TOPIC_ALIAS ( 2U ) +#define TEST_MSG_EXPIRY ( 100U ) + + +#define MQTT_TEST_UTF8_STRING ( "test" ) +#define MQTT_TEST_UTF8_STRING_LENGTH ( sizeof( MQTT_TEST_UTF8_STRING ) - 1 ) +#define MQTT_TEST_UINT8 ( 1U ) +#define MQTT_TEST_UINT16 ( 32U ) +#define MQTT_TEST_UINT32 ( 300U ) /* MQTT CONNECT flags. */ -#define MQTT_CONNECT_FLAG_CLEAN ( 1 ) /**< @brief Clean session. */ -#define MQTT_CONNECT_FLAG_WILL ( 2 ) /**< @brief Will present. */ -#define MQTT_CONNECT_FLAG_WILL_QOS1 ( 3 ) /**< @brief Will QoS 1. */ -#define MQTT_CONNECT_FLAG_WILL_QOS2 ( 4 ) /**< @brief Will QoS 2. */ -#define MQTT_CONNECT_FLAG_WILL_RETAIN ( 5 ) /**< @brief Will retain. */ -#define MQTT_CONNECT_FLAG_PASSWORD ( 6 ) /**< @brief Password present. */ -#define MQTT_CONNECT_FLAG_USERNAME ( 7 ) /**< @brief User name present. */ +#define MQTT_CONNECT_FLAG_CLEAN ( 1 ) /**< @brief Clean session. */ +#define MQTT_CONNECT_FLAG_WILL ( 2 ) /**< @brief Will present. */ +#define MQTT_CONNECT_FLAG_WILL_QOS1 ( 3 ) /**< @brief Will QoS 1. */ +#define MQTT_CONNECT_FLAG_WILL_QOS2 ( 4 ) /**< @brief Will QoS 2. */ +#define MQTT_CONNECT_FLAG_WILL_RETAIN ( 5 ) /**< @brief Will retain. */ +#define MQTT_CONNECT_FLAG_PASSWORD ( 6 ) /**< @brief Password present. */ +#define MQTT_CONNECT_FLAG_USERNAME ( 7 ) /**< @brief User name present. */ + +/*Default connect properties. */ +#define DEFAULT_RECEIVE_MAX ( 65535U ) +#define DEFAULT_REQUEST_PROBLEM ( 1 ) /** * @brief The Remaining Length field of MQTT disconnect packets, per MQTT spec. */ -#define MQTT_DISCONNECT_REMAINING_LENGTH ( ( uint8_t ) 0 ) +#define MQTT_DISCONNECT_REMAINING_LENGTH ( ( uint8_t ) 0 ) /** * @brief Set a bit in an 8-bit unsigned integer. @@ -152,17 +165,125 @@ struct NetworkContext */ #define MQTT_TEST_BUFFER_LENGTH ( 1024 ) -static uint8_t remainingLengthBuffer[ MQTT_REMAINING_BUFFER_MAX_LENGTH ] = { 0 }; - -static uint8_t encodedStringBuffer[ MQTT_TEST_BUFFER_LENGTH ] = { 0 }; - -static uint8_t mqttBuffer[ MQTT_TEST_BUFFER_LENGTH ] = { 0 }; +#define UINT16_DECODE( ptr ) \ + ( uint16_t ) ( ( ( ( uint16_t ) ptr[ 0 ] ) << 8 ) | \ + ( ( uint16_t ) ptr[ 1 ] ) ) + +#define UINT32_DECODE( ptr ) \ + ( uint32_t ) ( ( ( ( uint32_t ) ptr[ 0 ] ) << 24 ) | \ + ( ( ( uint32_t ) ptr[ 1 ] ) << 16 ) | \ + ( ( ( uint32_t ) ptr[ 2 ] ) << 8 ) | \ + ( ( uint32_t ) ptr[ 3 ] ) ) + +#define UINT32_BYTE3( x ) ( ( uint8_t ) ( ( x ) >> 24 ) ) + +#define UINT32_BYTE2( x ) ( ( uint8_t ) ( ( x ) >> 16 ) ) + +#define UINT32_BYTE1( x ) ( ( uint8_t ) ( ( x ) >> 8 ) ) + +#define UINT32_BYTE0( x ) ( ( uint8_t ) ( ( x ) & 0x000000FFU ) ) + + +#define MQTT_MAX_PACKET_SIZE ( 268435460UL ) +#define MQTT_VERSION_5 ( 5U ) +#define MQTT_SESSION_EXPIRY_SIZE ( 5U ) +#define MQTT_RECEIVE_MAX_SIZE ( 3U ) +#define MQTT_MAX_PACKET_PROPERTY_SIZE ( 5U ) +#define MQTT_TOPIC_ALIAS_SIZE ( 3U ) +#define MQTT_REQUEST_RESPONSE_SIZE ( 2U ) +#define MQTT_REQUEST_PROBLEM_SIZE ( 2U ) + +#define MQTT_SESSION_EXPIRY_ID ( 0x11 ) +#define MQTT_RECEIVE_MAX_ID ( 0x21 ) +#define MQTT_MAX_PACKET_SIZE_ID ( 0x27 ) +#define MQTT_TOPIC_ALIAS_MAX_ID ( 0x22 ) +#define MQTT_REQUEST_RESPONSE_ID ( 0x19 ) +#define MQTT_REQUEST_PROBLEM_ID ( 0x17 ) +#define MQTT_USER_PROPERTY_ID ( 0x26 ) +#define MQTT_AUTH_METHOD_ID ( 0x15 ) +#define MQTT_AUTH_DATA_ID ( 0x16 ) + +#define MQTT_WILL_DELAY_ID ( 0x18 ) +#define MQTT_PAYLOAD_FORMAT_ID ( 0x01 ) +#define MQTT_MSG_EXPIRY_ID ( 0x02 ) +#define MQTT_CONTENT_TYPE_ID ( 0x03 ) +#define MQTT_RESPONSE_TOPIC_ID ( 0x08 ) +#define MQTT_CORRELATION_DATA_ID ( 0x09 ) + +#define MQTT_MAX_QOS_ID ( 0x24 ) +#define MQTT_RETAIN_AVAILABLE_ID ( 0x25 ) +#define MQTT_ASSIGNED_CLIENT_ID ( 0x12 ) +#define MQTT_REASON_STRING_ID ( 0x1F ) +#define MQTT_WILDCARD_ID ( 0x28 ) +#define MQTT_SUB_AVAILABLE_ID ( 0x29 ) +#define MQTT_SHARED_SUB_ID ( 0x2A ) +#define MQTT_SERVER_KEEP_ALIVE_ID ( 0x13 ) +#define MQTT_RESPONSE_INFO_ID ( 0x1A ) +#define MQTT_SERVER_REF_ID ( 0x1C ) + +#define MQTT_REASON_SUCCESS ( 0x00 ) +#define MQTT_REASON_SEND_WILL ( 0x04 ) +#define MQTT_REASON_NO_MATCHING_SUBSCRIBERS ( 0x10 ) +#define MQTT_REASON_UNSPECIFIED_ERR ( 0x80 ) +#define MQTT_REASON_MALFORMED_PACKET ( 0x81 ) +#define MQTT_REASON_PROTOCOL_ERR ( 0x82 ) +#define MQTT_REASON_IMPL_SPECIFIC_ERR ( 0x83 ) +#define MQTT_REASON_UNSUPPORTED_PROTO_VER ( 0x84 ) +#define MQTT_REASON_CLIENT_ID_NOT_VALID ( 0x85 ) +#define MQTT_REASON_BAD_USER_OR_PASS ( 0x86 ) +#define MQTT_REASON_NOT_AUTHORIZED ( 0x87 ) +#define MQTT_REASON_SERVER_UNAVAILABLE ( 0x88 ) +#define MQTT_REASON_SERVER_BUSY ( 0x89 ) +#define MQTT_REASON_BANNED ( 0x8A ) +#define MQTT_REASON_SERVER_SHUTTING_DOWN ( 0x8B ) +#define MQTT_REASON_BAD_AUTH_METHOD ( 0x8C ) +#define MQTT_REASON_KEEP_ALIVE_TIMEOUT ( 0x8D ) +#define MQTT_REASON_SESSION_TAKEN_OVER ( 0x8E ) +#define MQTT_REASON_TOPIC_FILTER_INVALID ( 0x8F ) +#define MQTT_REASON_TOPIC_NAME_INVALID ( 0x90 ) +#define MQTT_REASON_PACKET_ID_IN_USE ( 0x91 ) +#define MQTT_REASON_PACKET_ID_NOT_FOUND ( 0x92 ) +#define MQTT_REASON_RX_MAX_EXCEEDED ( 0x93 ) +#define MQTT_REASON_TOPIC_ALIAS_INVALID ( 0x94 ) +#define MQTT_REASON_PACKET_TOO_LARGE ( 0x95 ) +#define MQTT_REASON_MSG_RATE_TOO_HIGH ( 0x96 ) +#define MQTT_REASON_QUOTA_EXCEEDED ( 0x97 ) +#define MQTT_REASON_ADMIN_ACTION ( 0x98 ) +#define MQTT_REASON_PAYLOAD_FORMAT_INVALID ( 0x99 ) +#define MQTT_REASON_RETAIN_NOT_SUPPORTED ( 0x9A ) +#define MQTT_REASON_QOS_NOT_SUPPORTED ( 0x9B ) +#define MQTT_REASON_USE_ANOTHER_SERVER ( 0x9C ) +#define MQTT_REASON_SERVER_MOVED ( 0x9D ) +#define MQTT_REASON_SS_NOT_SUPPORTED ( 0x9E ) +#define MQTT_REASON_CON_RATE_EXCEED ( 0x9F ) +#define MQTT_REASON_MAX_CON_TIME ( 0xA0 ) +#define MQTT_REASON_SUB_ID_NOT_SUP ( 0xA1 ) +#define MQTT_REASON_WILDCARD_SUB_NOT_SUP ( 0xA2 ) + +#define CORE_MQTT_ID_SIZE ( 1U ) +#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 ) +#define MQTT_SUBSCRIPTION_ID_ID ( 0x0B ) +#define MQTT_TOPIC_ALIAS_ID ( 0x23 ) + + +/* Variables common to testcases */ +MQTTConnectProperties_t properties; +MQTTUserProperty_t userProperty; +MQTTPublishInfo_t publishInfo; +MQTTConnectInfo_t connectInfo; +MQTTPacketInfo_t packetInfo; +MQTTStatus_t status; /* ============================ UNITY FIXTURES ============================ */ /* Called before each test method. */ void setUp( void ) { + memset( &properties, 0x0, sizeof( properties ) ); + memset( &userProperty, 0x0, sizeof( userProperty ) ); + memset( &publishInfo, 0x0, sizeof( publishInfo ) ); + memset( &connectInfo, 0x0, sizeof( connectInfo ) ); + memset( &packetInfo, 0x0, sizeof( packetInfo ) ); } /* Called after each test method. */ @@ -186,9 +307,9 @@ int suiteTearDown( int numFailures ) /** * @brief Mock successful transport receive by reading data from a buffer. */ -static int32_t mockReceive( NetworkContext_t * pNetworkContext, - void * pBuffer, - size_t bytesToRecv ) +int32_t mockReceive( NetworkContext_t * pNetworkContext, + void * pBuffer, + size_t bytesToRecv ) { uint8_t * returnBuffer = ( uint8_t * ) pBuffer; uint8_t * mockNetwork; @@ -216,7 +337,7 @@ static int32_t mockReceiveNoData( NetworkContext_t * pNetworkContext, void * pBuffer, size_t bytesToRecv ) { - /* Suppress unused parameter warning. */ +/* Suppress unused parameter warning. */ ( void ) pNetworkContext; ( void ) pBuffer; ( void ) bytesToRecv; @@ -231,7 +352,7 @@ static int32_t mockReceiveFailure( NetworkContext_t * pNetworkContext, void * pBuffer, size_t bytesToRecv ) { - /* Suppress unused parameter warning. */ +/* Suppress unused parameter warning. */ ( void ) pNetworkContext; ( void ) pBuffer; ( void ) bytesToRecv; @@ -264,33 +385,6 @@ static int32_t mockReceiveSucceedThenFail( NetworkContext_t * pNetworkContext, /* ========================================================================== */ -/** - * @brief Initialize pNetworkBuffer using static buffer. - * - * @param[in] pNetworkBuffer Network buffer provided for the context. - */ -static void setupNetworkBuffer( MQTTFixedBuffer_t * const pNetworkBuffer ) -{ - pNetworkBuffer->pBuffer = mqttBuffer; - pNetworkBuffer->size = MQTT_TEST_BUFFER_LENGTH; -} - -/** - * @brief Initialize pConnectInfo using test-defined macros. - * - * @param[in] pConnectInfo MQTT CONNECT packet parameters. - */ -static void setupConnectInfo( MQTTConnectInfo_t * const pConnectInfo ) -{ - pConnectInfo->cleanSession = true; - pConnectInfo->pClientIdentifier = MQTT_CLIENT_IDENTIFIER; - pConnectInfo->clientIdentifierLength = MQTT_CLIENT_IDENTIFIER_LEN; - pConnectInfo->keepAliveSeconds = 0; - pConnectInfo->pUserName = MQTT_TEST_USERNAME; - pConnectInfo->userNameLength = MQTT_TEST_USERNAME_LEN; - pConnectInfo->pPassword = MQTT_TEST_PASSWORD; - pConnectInfo->passwordLength = MQTT_TEST_PASSWORD_LEN; -} /** * @brief Initialize pPublishInfo using test-defined macros. @@ -386,6 +480,47 @@ static size_t encodeString( uint8_t * pDestination, return ( size_t ) ( pBuffer - pDestination ); } +/** + * @brief Encode UTF-8 string and its length into pDestination for + * packet serialization. + * + * @param[in] pDestination Buffer to write encoded string. + * @param[in] source String to encode. + * @param[in] sourceLength Length of the string to encode. + */ +static size_t encodeStringSize( uint8_t * pDestination, + const char * source, + uint16_t sourceLength ) +{ + uint8_t * pBuffer = NULL; + + /* Typecast const char * typed source buffer to const uint8_t *. + * This is to use same type buffers in memcpy. */ + const uint8_t * pSourceBuffer = ( const uint8_t * ) source; + + TEST_ASSERT_NOT_NULL( pSourceBuffer ); + TEST_ASSERT_NOT_NULL( pDestination ); + + pBuffer = pDestination; + + /* The first byte of a UTF-8 string is the high byte of the string length. */ + *pBuffer = UINT16_HIGH_BYTE( sourceLength ); + pBuffer++; + + /* The second byte of a UTF-8 string is the low byte of the string length. */ + *pBuffer = UINT16_LOW_BYTE( sourceLength ); + pBuffer++; + + /* Copy the string into pBuffer. */ + ( void ) memcpy( pBuffer, pSourceBuffer, sourceLength ); + + /* Return the pointer to the end of the encoded string. */ + pBuffer += sourceLength; + + return ( size_t ) ( pBuffer - pDestination ); +} + + /** * @brief Pad beginning and end of buffer with non-zero bytes to be used in * checking for under/overflow after serialization. @@ -427,60 +562,691 @@ static void checkBufferOverflow( uint8_t * pBuffer, BUFFER_PADDING_LENGTH ); } -/* ========================================================================== */ -/** - * @brief Tests that MQTT_GetConnectPacketSize works as intended. - */ -void test_MQTT_GetConnectPacketSize( void ) +static uint8_t * initializeDeserialize( MQTTPacketInfo_t * packetInfo, + uint8_t * pIndex ) +{ + uint8_t * pIndexLocal = pIndex; + + packetInfo->pRemainingData = pIndexLocal; + packetInfo->type = MQTT_PACKET_TYPE_CONNACK; + *pIndexLocal = 0x01; + pIndexLocal++; + *pIndexLocal = 0x00; + pIndexLocal++; + return pIndexLocal; +} + +static uint8_t * serializeuint_32( uint8_t * pIndex, + uint8_t propertyId ) +{ + uint8_t * pIndexLocal = pIndex; + + *pIndexLocal = propertyId; + pIndexLocal++; + pIndexLocal[ 0 ] = UINT32_BYTE3( MQTT_TEST_UINT32 ); + pIndexLocal[ 1 ] = UINT32_BYTE2( MQTT_TEST_UINT32 ); + pIndexLocal[ 2 ] = UINT32_BYTE1( MQTT_TEST_UINT32 ); + pIndexLocal[ 3 ] = UINT32_BYTE0( MQTT_TEST_UINT32 ); + pIndexLocal = &pIndexLocal[ 4 ]; + return pIndexLocal; +} + + +static uint8_t * serializeuint_16( uint8_t * pIndex, + uint8_t propertyId ) +{ + uint8_t * pIndexLocal = pIndex; + + *pIndexLocal = propertyId; + pIndexLocal++; + pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( MQTT_TEST_UINT16 ); + pIndexLocal[ 1 ] = UINT16_LOW_BYTE( MQTT_TEST_UINT16 ); + pIndexLocal = &pIndexLocal[ 2 ]; + return pIndexLocal; +} + +static uint8_t * serializeuint_8( uint8_t * pIndex, + uint8_t propertyId ) +{ + uint8_t * pIndexLocal = pIndex; + + *pIndexLocal = propertyId; + pIndexLocal++; + pIndexLocal[ 0 ] = MQTT_TEST_UINT8; + pIndexLocal++; + return pIndexLocal; +} +static uint8_t * serializeutf_8( uint8_t * pIndex, + uint8_t propertyId ) +{ + uint8_t * pIndexLocal = pIndex; + + *pIndexLocal = propertyId; + pIndexLocal++; + size_t dummy = encodeStringSize( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH ); + pIndexLocal = &pIndexLocal[ dummy ]; + return pIndexLocal; +} + +static uint8_t * serializeutf_8pair( uint8_t * pIndex ) +{ + uint8_t * pIndexLocal = pIndex; + + *pIndexLocal = MQTT_USER_PROPERTY_ID; + pIndexLocal++; + size_t dummy = encodeStringSize( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH ); + pIndexLocal = &pIndexLocal[ dummy ]; + dummy = encodeStringSize( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH ); + pIndexLocal = &pIndexLocal[ dummy ]; + return pIndexLocal; +} + + +void test_MQTTV5_DeserializeConnackOnlyStatus( void ) +{ + uint8_t buffer[ 50 ]; + uint8_t * pIndex = buffer; + MQTTPropBuilder_t propBuffer = { 0 }; + + + status = MQTT_DeserializeAck( NULL, NULL, NULL, NULL, 0, 0, &propBuffer, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + bool sessionPresent; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + status = MQTT_DeserializeAck( NULL, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + status = MQTT_DeserializeAck( &packetInfo, NULL, NULL, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + packetInfo.type = MQTT_PACKET_TYPE_CONNACK; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + packetInfo.pRemainingData = pIndex; + packetInfo.type = MQTT_PACKET_TYPE_CONNECT; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + /*Reserved bit incorrect*/ + buffer[ 0 ] = 0x11; + packetInfo.type = MQTT_PACKET_TYPE_CONNACK; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + + /* + * Session Present Bit is set but reason code is not equal to 0; + */ + buffer[ 0 ] = 0x01; + buffer[ 1 ] = 0x01; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + + /* 5 + 1 + 2 = 8 */ + size_t propertyLength = encodeRemainingLength( pIndex, 5 ); + packetInfo.remainingLength = propertyLength + 7; + /*Not a valid reason code*/ + buffer[ 0 ] = 0x00; + buffer[ 1 ] = 0x03; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + /*All the valid response code*/ + buffer[ 1 ] = 0x80; + buffer[ 2 ] = 0; + properties.maxPacketSize = 100; + packetInfo.remainingLength = 3; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x80; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x81; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x82; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x83; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x80; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x84; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x80; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x85; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x86; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x87; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x88; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x89; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x8A; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x8C; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x88; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x90; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x95; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x97; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x99; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x9A; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x9A; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x9B; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x9C; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x9D; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + buffer[ 1 ] = 0x9F; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTServerRefused, status ); + + /*Exceeds the max packet size set by the client*/ + properties.maxPacketSize = 2; + buffer[ 1 ] = 0x00; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + + /*Validate the remaining length*/ + properties.maxPacketSize = MQTT_MAX_PACKET_SIZE; + packetInfo.remainingLength = 7; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 20; + pIndex = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndex, 20971556356235 ); + LogDebug( ( "Encoded size for length is %lu bytes.", + ( unsigned long ) propertyLength ) ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + pIndex = &buffer[ 2 ]; + *pIndex = 0x81; + pIndex++; + *pIndex = 0x00; + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + status = MQTT_DeserializeAck( &packetInfo, NULL, NULL, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_DeserializeAck( &packetInfo, NULL, &sessionPresent, NULL, 0, 0, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +void test_MQTTV5_DeserializeConnackOnlyuint_32( void ) +{ + uint8_t buffer[ 200 ] = { 0 }; + bool session = false; + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t * pIndexLocal = initializeDeserialize( &packetInfo, buffer ); + size_t propertyLength = encodeRemainingLength( pIndexLocal, 10 ); + + packetInfo.remainingLength = propertyLength + 12; + properties.maxPacketSize = 150; + pIndexLocal++; + pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID ); + pIndexLocal = serializeuint_32( pIndexLocal, MQTT_MAX_PACKET_SIZE_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT32, properties.sessionExpiry ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT32, properties.serverMaxPacketSize ); + + /*Test with NULL propBuffer. */ + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, NULL, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Protocol error to include the same property twice*/ + packetInfo.remainingLength = 13; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 10 ); + pIndexLocal++; + pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID ); + pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 7; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 4 ); + pIndexLocal++; + *pIndexLocal = MQTT_SESSION_EXPIRY_ID; + pIndexLocal++; + pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid id*/ + packetInfo.remainingLength = 8; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 5 ); + pIndexLocal++; + pIndexLocal = serializeuint_32( pIndexLocal, 0x00 ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /* Max packet size cannot have a value 0*/ + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 5 ); + packetInfo.remainingLength = propertyLength + 7; + pIndexLocal++; + *pIndexLocal = MQTT_MAX_PACKET_SIZE_ID; + pIndexLocal++; + pIndexLocal[ 0 ] = UINT32_BYTE3( 0 ); + pIndexLocal[ 1 ] = UINT32_BYTE2( 0 ); + pIndexLocal[ 2 ] = UINT32_BYTE1( 0 ); + pIndexLocal[ 3 ] = UINT32_BYTE0( 0 ); + pIndexLocal = &pIndexLocal[ 4 ]; + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} + +void test_MQTTV5_DeserializeConnackOnlyuint_16( void ) +{ + uint8_t buffer[ 200 ] = { 0 }; + uint8_t * pIndexLocal = buffer; + MQTTPropBuilder_t propBuffer = { 0 }; + + buffer[ 0 ] = 0x01; + buffer[ 1 ] = 0x00; + bool session = false; + packetInfo.pRemainingData = buffer; + packetInfo.type = MQTT_PACKET_TYPE_CONNACK; + pIndexLocal = &buffer[ 2 ]; + properties.maxPacketSize = MQTT_MAX_PACKET_SIZE; + size_t propertyLength = encodeRemainingLength( pIndexLocal, 9 ); + packetInfo.remainingLength = propertyLength + 11; + pIndexLocal++; + pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID ); + pIndexLocal = serializeuint_16( pIndexLocal, MQTT_TOPIC_ALIAS_MAX_ID ); + pIndexLocal = serializeuint_16( pIndexLocal, MQTT_SERVER_KEEP_ALIVE_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT16, properties.serverReceiveMax ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT16, properties.serverTopicAliasMax ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT16, properties.serverKeepAlive ); + + /*Receive Max cannot have a value 0*/ + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 3 ); + packetInfo.remainingLength = propertyLength + 5; + pIndexLocal++; + *pIndexLocal = MQTT_RECEIVE_MAX_ID; + pIndexLocal++; + pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( 0 ); + pIndexLocal[ 1 ] = UINT16_LOW_BYTE( 0 ); + pIndexLocal = &pIndexLocal[ 2 ]; + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Protocol error to include the same property twice*/ + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 6 ); + packetInfo.remainingLength = propertyLength + 8; + pIndexLocal++; + pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID ); + pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 5; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 2 ); + pIndexLocal++; + pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} + +void test_MQTTV5_DeserializeConnackOnlyuint_8( void ) +{ + uint8_t buffer[ 200 ] = { 0 }; + uint8_t * pIndexLocal = buffer; + MQTTPropBuilder_t propBuffer = { 0 }; + + buffer[ 0 ] = 0x01; + buffer[ 1 ] = 0x00; + bool session = false; + packetInfo.pRemainingData = buffer; + packetInfo.type = MQTT_PACKET_TYPE_CONNACK; + packetInfo.remainingLength = 13; + pIndexLocal = &buffer[ 2 ]; + properties.maxPacketSize = MQTT_MAX_PACKET_SIZE; + size_t propertyLength = encodeRemainingLength( pIndexLocal, 10 ); + pIndexLocal++; + pIndexLocal = serializeuint_8( pIndexLocal, MQTT_MAX_QOS_ID ); + pIndexLocal = serializeuint_8( pIndexLocal, MQTT_RETAIN_AVAILABLE_ID ); + pIndexLocal = serializeuint_8( pIndexLocal, MQTT_WILDCARD_ID ); + pIndexLocal = serializeuint_8( pIndexLocal, MQTT_SHARED_SUB_ID ); + pIndexLocal = serializeuint_8( pIndexLocal, MQTT_SUB_AVAILABLE_ID ); + + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.serverMaxQos ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.retainAvailable ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.isWildcardAvailable ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.isSharedAvailable ); + TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.isSubscriptionIdAvailable ); + + /*Protocol error to have a value other than 0 or 1*/ + packetInfo.remainingLength = 5; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 2 ); + pIndexLocal++; + *pIndexLocal = MQTT_MAX_QOS_ID; + pIndexLocal++; + pIndexLocal[ 0 ] = 3; + pIndexLocal++; + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Protocol error to include the same property twice*/ + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 4 ); + packetInfo.remainingLength = propertyLength + 6; + pIndexLocal++; + pIndexLocal = serializeuint_8( pIndexLocal, MQTT_MAX_QOS_ID ); + pIndexLocal = serializeuint_8( pIndexLocal, MQTT_MAX_QOS_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 4; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 1 ); + pIndexLocal++; + *pIndexLocal = MQTT_MAX_QOS_ID; + pIndexLocal++; + pIndexLocal[ 0 ] = 0; + pIndexLocal++; + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} + + +void test_MQTTV5_DeserializeConnackOnlyutf_8( void ) +{ + uint8_t buffer[ 200 ] = { 0 }; + uint8_t * pIndexLocal = buffer; + MQTTPropBuilder_t propBuffer; + + buffer[ 0 ] = 0x01; + buffer[ 1 ] = 0x00; + + bool session = false; + packetInfo.pRemainingData = buffer; + packetInfo.type = MQTT_PACKET_TYPE_CONNACK; + pIndexLocal = &buffer[ 2 ]; + properties.requestResponseInfo = 1; + properties.maxPacketSize = MQTT_MAX_PACKET_SIZE; + size_t propertyLength = encodeRemainingLength( pIndexLocal, 28 ); + packetInfo.remainingLength = propertyLength + 28 + 2; + pIndexLocal++; + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID ); + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_REASON_STRING_ID ); + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_RESPONSE_INFO_ID ); + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_SERVER_REF_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Protocol error to include the same property twice*/ + packetInfo.remainingLength = 17; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 14 ); + pIndexLocal++; + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID ); + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 7; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 4 ); + pIndexLocal++; + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 2 ); + packetInfo.remainingLength = propertyLength + 4; + pIndexLocal++; + serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Protocol error to include response information if is is set to false by client*/ + properties.requestResponseInfo = 0; + packetInfo.remainingLength = 10; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 7 ); + pIndexLocal++; + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_RESPONSE_INFO_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + packetInfo.remainingLength = 10; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 7 ); + pIndexLocal++; + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_AUTH_METHOD_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 7 ); + pIndexLocal++; + pIndexLocal = serializeutf_8( pIndexLocal, MQTT_AUTH_DATA_ID ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + + +void test_MQTTV5_DeserializeConnackOnlyUserProperty( void ) +{ + /* change this, it is not put in the struct at all. */ + + uint8_t buffer[ 70000 ] = { 0 }; + uint8_t * pIndexLocal = buffer; + + buffer[ 0 ] = 0x01; + buffer[ 1 ] = 0x00; + MQTTPropBuilder_t propBuffer = { 0 }; + + bool session = false; + properties.maxPacketSize = MQTT_MAX_PACKET_SIZE; + packetInfo.pRemainingData = buffer; + packetInfo.type = MQTT_PACKET_TYPE_CONNACK; + packetInfo.remainingLength = 16; + pIndexLocal = &buffer[ 2 ]; + size_t propertyLength = encodeRemainingLength( pIndexLocal, 13 ); + pIndexLocal++; + pIndexLocal = serializeutf_8pair( pIndexLocal ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 5; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 2 ); + pIndexLocal++; + pIndexLocal = serializeutf_8pair( pIndexLocal ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 6; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 3 ); + pIndexLocal++; + pIndexLocal = serializeutf_8pair( pIndexLocal ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 8 ); + packetInfo.remainingLength = propertyLength + 10; + pIndexLocal++; + pIndexLocal = serializeutf_8pair( pIndexLocal ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid property length*/ + packetInfo.remainingLength = 15; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 12 ); + pIndexLocal++; + pIndexLocal = serializeutf_8pair( pIndexLocal ); + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Discard user property*/ + packetInfo.remainingLength = 65018; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 65013 ); + pIndexLocal += 3; + uint32_t i = 0U; + + for( ; i < 5001; i++ ) + { + pIndexLocal = serializeutf_8pair( pIndexLocal ); + } + + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + packetInfo.remainingLength = 65017; + pIndexLocal = &buffer[ 2 ]; + propertyLength = encodeRemainingLength( pIndexLocal, 65012 ); + pIndexLocal += 3; + + for( ; i < 5001; i++ ) + { + pIndexLocal = serializeutf_8pair( pIndexLocal ); + } + + status = MQTT_DeserializeAck( &packetInfo, NULL, &session, NULL, 0, 0, &propBuffer, &properties ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} + + + +void test_MQTTV5_GetConnectPacketSize( void ) { - MQTTConnectInfo_t connectInfo; size_t remainingLength = 0; size_t packetSize = 0; - MQTTStatus_t status = MQTTSuccess; - MQTTPublishInfo_t willInfo = { 0 }; + /* Call MQTT_GetConnectPacketSize() with various combinations of * incorrect paramters */ - - status = MQTT_GetConnectPacketSize( NULL, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( NULL, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, NULL, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, NULL ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Verify empty connect info fails. */ memset( &connectInfo, 0x0, sizeof( connectInfo ) ); - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Verify empty client identifier fails. */ connectInfo.pClientIdentifier = CLIENT_IDENTIFIER; connectInfo.clientIdentifierLength = 0; - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); connectInfo.pClientIdentifier = NULL; connectInfo.clientIdentifierLength = CLIENT_IDENTIFIER_LENGTH; - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Test a will message payload length that is too large. */ - memset( &connectInfo, 0x0, sizeof( connectInfo ) ); connectInfo.pClientIdentifier = CLIENT_IDENTIFIER; connectInfo.clientIdentifierLength = UINT16_MAX; connectInfo.pPassword = ""; connectInfo.passwordLength = UINT16_MAX; connectInfo.pUserName = ""; connectInfo.userNameLength = UINT16_MAX; - willInfo.pTopicName = TEST_TOPIC_NAME; - willInfo.topicNameLength = UINT16_MAX; + publishInfo.pTopicName = TEST_TOPIC_NAME; + publishInfo.topicNameLength = UINT16_MAX; /* A valid will message payload is less than the maximum 16 bit integer. */ - willInfo.payloadLength = UINT16_MAX + 2; - status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, &remainingLength, &packetSize ); + publishInfo.payloadLength = UINT16_MAX + 2; + status = MQTT_GetConnectPacketSize( &connectInfo, &publishInfo, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTBadParameter, status ); /* Verify good case */ @@ -488,26 +1254,33 @@ void test_MQTT_GetConnectPacketSize( void ) connectInfo.cleanSession = true; connectInfo.pClientIdentifier = "TEST"; connectInfo.clientIdentifierLength = 4; - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Make sure remaining size returned is 16. */ - TEST_ASSERT_EQUAL_INT( 16, remainingLength ); - /* Make sure packet size is 18. */ - TEST_ASSERT_EQUAL_INT( 18, packetSize ); + /* Make sure remaining size returned is 17. */ + TEST_ASSERT_EQUAL_INT( 17, remainingLength ); + /* Make sure packet size is 19. */ + TEST_ASSERT_EQUAL_INT( 19, packetSize ); + + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.currentIndex = MQTT_MAX_REMAINING_LENGTH; + + status = MQTT_GetConnectPacketSize( &connectInfo, &publishInfo, &propBuffer, &propBuffer, &remainingLength, &packetSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); /* With will. These parameters will cause the packet to be * 4 + 2 + 8 + 2 = 16 bytes larger. */ - memset( &willInfo, 0x0, sizeof( willInfo ) ); - willInfo.pTopicName = "test"; - willInfo.topicNameLength = 4; - willInfo.pPayload = "testload"; - willInfo.payloadLength = 8; - status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, &remainingLength, &packetSize ); + publishInfo.pTopicName = "test"; + publishInfo.topicNameLength = 4; + publishInfo.pPayload = "testload"; + publishInfo.payloadLength = 8; + status = MQTT_GetConnectPacketSize( &connectInfo, &publishInfo, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Make sure remaining size returned is 32 = 16 + 16. */ - TEST_ASSERT_EQUAL_INT( 32, remainingLength ); - /* Make sure packet size is 34 = 18 + 16. */ - TEST_ASSERT_EQUAL_INT( 34, packetSize ); + /* Make sure remaining size returned is 32 = 16 + 16 + 2. */ + TEST_ASSERT_EQUAL_INT( 34, remainingLength ); + /* Make sure packet size is 34 = 18 + 16 + 2. */ + TEST_ASSERT_EQUAL_INT( 36, packetSize ); /* With username and password. This will add 4 + 2 + 4 + 2 = 12 bytes. */ connectInfo.cleanSession = true; @@ -515,12 +1288,21 @@ void test_MQTT_GetConnectPacketSize( void ) connectInfo.userNameLength = 4; connectInfo.pPassword = "PASS"; connectInfo.passwordLength = 4; - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); /* Make sure remaining size returned is 28 = 16 + 12. */ - TEST_ASSERT_EQUAL_INT( 28, remainingLength ); + TEST_ASSERT_EQUAL_INT( 29, remainingLength ); /* Make sure packet size is 30 = 18 + 12. */ - TEST_ASSERT_EQUAL_INT( 30, packetSize ); + TEST_ASSERT_EQUAL_INT( 31, packetSize ); + + propBuffer.pBuffer = NULL; + status = MQTT_GetConnectPacketSize( &connectInfo, &publishInfo, &propBuffer, &propBuffer, &remainingLength, &packetSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + propBuffer.pBuffer = buf; + propBuffer.currentIndex = MQTT_MAX_REMAINING_LENGTH + 1; + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &propBuffer, NULL, &remainingLength, &packetSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); } /** @@ -538,13 +1320,13 @@ void test_MQTT_SerializeConnect( void ) MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; /* Verify bad parameter errors. */ - status = MQTT_SerializeConnect( NULL, &willInfo, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( NULL, &willInfo, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_SerializeConnect( &connectInfo, &willInfo, remainingLength, NULL ); + status = MQTT_SerializeConnect( &connectInfo, &willInfo, NULL, NULL, remainingLength, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); memset( &connectInfo, 0x0, sizeof( connectInfo ) ); - status = MQTT_SerializeConnect( &connectInfo, NULL, 120, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, NULL, NULL, NULL, 120, &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); /* Create a good connection info. */ @@ -554,7 +1336,7 @@ void test_MQTT_SerializeConnect( void ) /* Inject a invalid fixed buffer test with a good connectInfo. */ memset( &fixedBuffer, 0x0, sizeof( fixedBuffer ) ); - status = MQTT_SerializeConnect( &connectInfo, NULL, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, NULL, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Good case succeeds. */ @@ -563,13 +1345,13 @@ void test_MQTT_SerializeConnect( void ) fixedBuffer.size = bufferSize; /* Calculate a good packet size. */ - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); /* Make sure buffer has enough space */ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); /* Make sure test succeeds. */ padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeConnect( &connectInfo, NULL, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, NULL, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); @@ -579,11 +1361,11 @@ void test_MQTT_SerializeConnect( void ) connectInfo.userNameLength = 4; connectInfo.pPassword = "PASS"; connectInfo.passwordLength = 4; - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeConnect( &connectInfo, NULL, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, NULL, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); @@ -594,10 +1376,10 @@ void test_MQTT_SerializeConnect( void ) willInfo.qos = MQTTQoS1; willInfo.pPayload = "test"; willInfo.payloadLength = ( uint16_t ) strlen( willInfo.pPayload ); - status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); - status = MQTT_SerializeConnect( &connectInfo, &willInfo, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, &willInfo, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL( MQTTBadParameter, status ); /* Success. */ @@ -608,22 +1390,22 @@ void test_MQTT_SerializeConnect( void ) willInfo.topicNameLength = ( uint16_t ) strlen( willInfo.pTopicName ); willInfo.pPayload = "test"; willInfo.payloadLength = ( uint16_t ) strlen( willInfo.pPayload ); - status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeConnect( &connectInfo, &willInfo, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, &willInfo, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); /* Again with QoS 2 and 0. */ willInfo.qos = MQTTQoS2; - status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeConnect( &connectInfo, &willInfo, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, &willInfo, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); @@ -632,11 +1414,11 @@ void test_MQTT_SerializeConnect( void ) /* NULL payload is acceptable. */ willInfo.pPayload = NULL; willInfo.payloadLength = 0; - status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeConnect( &connectInfo, &willInfo, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, &willInfo, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); @@ -646,535 +1428,283 @@ void test_MQTT_SerializeConnect( void ) /* Throwing in a possible valid zero length password. */ connectInfo.pPassword = "PASS"; connectInfo.passwordLength = 0; - status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &remainingLength, &packetSize ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, NULL, NULL, &remainingLength, &packetSize ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); /* Set the fixed buffer to exactly the size of the packet. */ fixedBuffer.size = packetSize; padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeConnect( &connectInfo, NULL, remainingLength, &fixedBuffer ); + status = MQTT_SerializeConnect( &connectInfo, NULL, NULL, NULL, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); -} -/* ========================================================================== */ - -/** - * @brief Tests that MQTT_GetSubscribePacketSize works as intended. - */ -void test_MQTT_GetSubscribePacketSize( void ) -{ - MQTTSubscribeInfo_t subscriptionList; - size_t subscriptionCount = 1; - size_t remainingLength = 0; - size_t packetSize = 0; - MQTTStatus_t status = MQTTSuccess; - MQTTSubscribeInfo_t fourThousandSubscriptions[ 4096 ] = { 0 }; - int i; + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); - /* Verify parameters. */ - - status = MQTT_GetSubscribePacketSize( NULL, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + MQTTPropAdd_SessionExpiry( &propBuffer, 100 ); + status = MQTT_GetConnectPacketSize( &connectInfo, NULL, &propBuffer, NULL, &remainingLength, &packetSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + /* Set the fixed buffer to exactly the size of the packet. */ + fixedBuffer.size = packetSize; + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializeConnect( &connectInfo, NULL, &propBuffer, NULL, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); - status = MQTT_GetSubscribePacketSize( &subscriptionList, - subscriptionCount, - NULL, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + /* Testing with will property */ + propBuffer.currentIndex = 0; + MQTTPropAdd_PubMessageExpiry( &propBuffer, 100 ); - status = MQTT_GetSubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_GetConnectPacketSize( &connectInfo, &willInfo, NULL, &propBuffer, &remainingLength, &packetSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + /* Set the fixed buffer to exactly the size of the packet. */ + fixedBuffer.size = packetSize; + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializeConnect( &connectInfo, &willInfo, NULL, &propBuffer, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + /*Test with null buffer*/ + propBuffer.pBuffer = NULL; + status = MQTT_SerializeConnect( &connectInfo, &willInfo, NULL, &propBuffer, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + status = MQTT_SerializeConnect( &connectInfo, &willInfo, &propBuffer, NULL, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); +} - /* Verify empty subscription list fails. */ - memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); - subscriptionCount = 0; - status = MQTT_GetSubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +void test_RemaininglengthLimit( void ) +{ + /* Test will property length more than the max value allowed. */ + size_t remainingLength = 0; + size_t packetSize = 0; + uint32_t maxPacketSize = 100; + MQTTStatus_t status = MQTTSuccess; - /* Zero length topic filter. */ - subscriptionCount = 1; - status = MQTT_GetSubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + publishInfo.topicNameLength = 0U; + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.currentIndex = MQTT_MAX_REMAINING_LENGTH; /* property length == max_remaining_length */ - /* NULL topic filter, nonzero length. */ - subscriptionList.topicFilterLength = 1; - status = MQTT_GetSubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); + status = MQTT_GetPublishPacketSize( &publishInfo, &propBuffer, &remainingLength, &packetSize, MQTT_MAX_REMAINING_LENGTH ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + status = MQTT_GetDisconnectPacketSize( &propBuffer, &remainingLength, &packetSize, maxPacketSize, 0x00 ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} - /* Verify packet size cannot exceed limit. Note the max remaining length of - * an MQTT packet is 2^28-1 = 268435455, or 256MiB. Since the only way to increase - * the subscribe packet size is with the topic filters of the subscriptions - * (the lengths of which are only 2 bytes), we need at least - * 2^28 / 2^16 = 2^12 = 4096 of them. */ - for( i = 0; i < 4096; i++ ) - { - fourThousandSubscriptions[ i ].topicFilterLength = UINT16_MAX; +void test_MQTTV5_ValidatePublishParams() +{ + uint16_t topicAlias = 10U; + uint8_t maxQos = 0U; + uint8_t retain = 0U; + uint32_t maxPacketSize = 0U; - /* We need to set this to avoid an early bad parameter, however we do - * not need a 65535 byte buffer as the packet will not be serialized. */ - fourThousandSubscriptions[ i ].pTopicFilter = ""; - } + /*Publish info cannot be null*/ + status = MQTT_ValidatePublishParams( NULL, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - subscriptionCount = sizeof( fourThousandSubscriptions ) / sizeof( fourThousandSubscriptions[ 0 ] ); - status = MQTT_GetSubscribePacketSize( fourThousandSubscriptions, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + /*Retain is not allowed. */ + publishInfo.retain = true; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - /* Verify good case. */ - memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); - subscriptionList.qos = MQTTQoS0; - subscriptionList.pTopicFilter = "/example/topic"; - subscriptionList.topicFilterLength = sizeof( "/example/topic" ); - subscriptionCount = sizeof( subscriptionList ) / sizeof( MQTTSubscribeInfo_t ); - status = MQTT_GetSubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_GREATER_THAN( remainingLength, packetSize ); + /*Qos invalid*/ + publishInfo.retain = false; + publishInfo.qos = 1; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + /*Valid parameters should return success*/ + publishInfo.qos = 1; + maxQos = 1; + publishInfo.retain = true; + retain = 1; + publishInfo.pTopicName = "abc"; + publishInfo.topicNameLength = 3; + maxPacketSize = 10; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + publishInfo.pTopicName = NULL; + publishInfo.topicNameLength = 0; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + /*Invalid topic name and topic name length*/ + publishInfo.pTopicName = NULL; + publishInfo.topicNameLength = 2; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + /*Invalid maxPacket size*/ + publishInfo.pTopicName = "abc"; + publishInfo.topicNameLength = 3; + maxPacketSize = 0; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + maxPacketSize = 100; + topicAlias = 0; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + publishInfo.topicNameLength = 0; + status = MQTT_ValidatePublishParams( &publishInfo, retain, maxQos, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + publishInfo.pTopicName = "abc"; + publishInfo.topicNameLength = 3; + publishInfo.qos = 0; + status = MQTT_ValidatePublishParams( &publishInfo, retain, 0, topicAlias, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); } -/** - * @brief Tests that MQTT_GetUnsubscribePacketSize works as intended. - */ -void test_MQTT_GetUnsubscribePacketSize( void ) +void test_MQTTV5_GetPublishPacketSize() { - MQTTSubscribeInfo_t subscriptionList; - size_t subscriptionCount = 1; - size_t remainingLength = 0; - size_t packetSize = 0; - MQTTStatus_t status = MQTTSuccess; - MQTTSubscribeInfo_t fourThousandSubscriptions[ 4096 ] = { 0 }; - int i; + size_t remainingLength = 0U; + size_t packetSize = 0U; + uint32_t maxPacketSize = 0U; - /* Verify parameters. */ + setupPublishInfo( &publishInfo ); + /*Test with invalid paramters*/ + status = MQTT_GetPublishPacketSize( NULL, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - status = MQTT_GetUnsubscribePacketSize( NULL, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, NULL, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - status = MQTT_GetUnsubscribePacketSize( &subscriptionList, - subscriptionCount, - NULL, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, NULL, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - status = MQTT_GetUnsubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + /*Topic name invalid*/ + publishInfo.pTopicName = NULL; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - /* Verify empty subscription list fails. */ - memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); - subscriptionCount = 0; - status = MQTT_GetUnsubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + publishInfo.pTopicName = TEST_TOPIC_NAME; - /* Zero length topic filter. */ - subscriptionCount = 1; - status = MQTT_GetUnsubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + /*Topic alias is not allowed and topic name is not provided.*/ + publishInfo.topicNameLength = 0; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - /* NULL topic filter, nonzero length. */ - subscriptionList.topicFilterLength = 1; - status = MQTT_GetUnsubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + maxPacketSize = 100; + publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH; + /*Packet size too large*/ + publishInfo.payloadLength = MQTT_MAX_REMAINING_LENGTH; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - /* Verify packet size cannot exceed limit. Note the max remaining length of - * an MQTT packet is 2^28-1 = 268435455, or 256MiB. Since the only way to increase - * the subscribe packet size is with the topic filters of the subscriptions - * (the lengths of which are only 2 bytes), we need at least - * 2^28 / 2^16 = 2^12 = 4096 of them. */ - for( i = 0; i < 4096; i++ ) - { - fourThousandSubscriptions[ i ].topicFilterLength = UINT16_MAX; + publishInfo.payloadLength = MQTT_MAX_REMAINING_LENGTH - TEST_TOPIC_NAME_LENGTH - 4; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - /* We need to set this to avoid an early bad parameter, however we do - * not need a 65535 byte buffer as the packet will not be serialized. */ - fourThousandSubscriptions[ i ].pTopicFilter = ""; - } + /* Good case succeeds. */ + publishInfo.pPayload = ""; + publishInfo.payloadLength = 0; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); - subscriptionCount = sizeof( fourThousandSubscriptions ) / sizeof( fourThousandSubscriptions[ 0 ] ); - status = MQTT_GetUnsubscribePacketSize( fourThousandSubscriptions, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + /* Again with QoS 2. */ + publishInfo.qos = MQTTQoS2; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); - /* Verify good case. */ - memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); - subscriptionList.qos = MQTTQoS0; - subscriptionList.pTopicFilter = "/example/topic"; - subscriptionList.topicFilterLength = sizeof( "/example/topic" ); - subscriptionCount = sizeof( subscriptionList ) / sizeof( MQTTSubscribeInfo_t ); - status = MQTT_GetUnsubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_GREATER_THAN( remainingLength, packetSize ); + publishInfo.retain = true; + + /*Valid properties*/ + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 100 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); + MQTTPropAdd_PubTopicAlias( &propBuffer, 1 ); + + status = MQTT_GetPublishPacketSize( &publishInfo, &propBuffer, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + /* No topic name*/ + publishInfo.topicNameLength = 0U; + publishInfo.pTopicName = NULL; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + propBuffer.pBuffer = NULL; + status = MQTT_GetPublishPacketSize( &publishInfo, &propBuffer, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + propBuffer.pBuffer = buf; + + /*Packet size is more than the server allowed max packet size*/ + maxPacketSize = 4; + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, maxPacketSize ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); } /** - * @brief Tests that MQTT_SerializeSubscribe works as intended. + * @brief Tests that MQTT_SerializePublish works as intended. */ -void test_MQTT_SerializeSubscribe( void ) +void test_MQTT_SerializePublish( void ) { - MQTTSubscribeInfo_t subscriptionList; - size_t subscriptionCount = 1; - size_t remainingLength = 0; - uint8_t buffer[ 25 + 2 * BUFFER_PADDING_LENGTH ]; + MQTTPublishInfo_t publishInfo; + size_t remainingLength = 98; + uint8_t buffer[ 200 + 2 * BUFFER_PADDING_LENGTH ]; size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH; size_t packetSize = bufferSize; MQTTStatus_t status = MQTTSuccess; MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; - uint8_t expectedPacket[ 100 ]; - uint8_t * pIterator = expectedPacket; + uint8_t expectedPacket[ 200 ]; + uint8_t * pIterator; const uint16_t PACKET_ID = 1; + const char * longTopic = "/test/topic/name/longer/than/one/hundred/twenty/eight/characters" \ + "/test/topic/name/longer/than/one/hundred/twenty/eight/characters"; /* Verify bad parameters fail. */ - status = MQTT_SerializeSubscribe( NULL, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + memset( &publishInfo, 0x00, sizeof( publishInfo ) ); + publishInfo.pTopicName = "/test/topic"; + publishInfo.topicNameLength = sizeof( "/test/topic" ); - status = MQTT_SerializeSubscribe( &subscriptionList, - subscriptionCount, - 0, - remainingLength, - &fixedBuffer ); + status = MQTT_SerializePublish( NULL, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_SerializeSubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - NULL ); + status = MQTT_SerializePublish( &publishInfo, + NULL, + PACKET_ID, + remainingLength, + NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Verify a NULL buffer in the fixed buffer struct fails */ fixedBuffer.pBuffer = NULL; - status = MQTT_SerializeSubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); + status = MQTT_SerializePublish( &publishInfo, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Restore the fixed buffer. */ fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; - /* Get correct values of packet size and remaining length. */ - memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); - subscriptionList.qos = MQTTQoS0; - subscriptionList.pTopicFilter = "/example/topic"; - subscriptionList.topicFilterLength = sizeof( "/example/topic" ); - subscriptionCount = sizeof( subscriptionList ) / sizeof( MQTTSubscribeInfo_t ); - status = MQTT_GetSubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Make sure buffer has enough space */ - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); - - /* Make sure subscription count of zero fails. */ - status = MQTT_SerializeSubscribe( &subscriptionList, - 0, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - /* Test if buffer is too small. */ - fixedBuffer.size = 1; - status = MQTT_SerializeSubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); - fixedBuffer.size = bufferSize; - - /* Make sure success is returned for good case. */ - padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeSubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - checkBufferOverflow( buffer, sizeof( buffer ) ); - - /* MQTT SUBSCRIBE packet format: - * 0x82 (1 byte) - * Remaining length (1-4 bytes) - * Packet ID (2 bytes) - * Topic filters (series of 2 byte lengths followed by filter, then QoS) (variable) */ - *pIterator++ = MQTT_PACKET_TYPE_SUBSCRIBE; - pIterator += encodeRemainingLength( pIterator, remainingLength ); - *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); - *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); - pIterator += encodeString( pIterator, subscriptionList.pTopicFilter, subscriptionList.topicFilterLength ); - *pIterator++ = subscriptionList.qos; - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); -} - -/** - * @brief Tests that MQTT_SerializeUnsubscribe works as intended. - */ -void test_MQTT_SerializeUnsubscribe( void ) -{ - MQTTSubscribeInfo_t subscriptionList; - size_t subscriptionCount = 1; - size_t remainingLength = 0; - uint8_t buffer[ 25 + 2 * BUFFER_PADDING_LENGTH ]; - size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH; - size_t packetSize = bufferSize; - MQTTStatus_t status = MQTTSuccess; - MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; - uint8_t expectedPacket[ 100 ]; - uint8_t * pIterator = expectedPacket; - - const uint16_t PACKET_ID = 1; - - status = MQTT_SerializeUnsubscribe( NULL, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - status = MQTT_SerializeUnsubscribe( &subscriptionList, - subscriptionCount, - 0, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - status = MQTT_SerializeUnsubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - /* Verify a NULL buffer in the fixed buffer struct fails */ - fixedBuffer.pBuffer = NULL; - status = MQTT_SerializeUnsubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - /* Restore the fixed buffer. */ - fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; - - /* Get correct values of packetsize and remaining length. */ - memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); - subscriptionList.qos = MQTTQoS0; - subscriptionList.pTopicFilter = "/example/topic"; - subscriptionList.topicFilterLength = sizeof( "/example/topic" ); - subscriptionCount = sizeof( subscriptionList ) / sizeof( MQTTSubscribeInfo_t ); - status = MQTT_GetUnsubscribePacketSize( &subscriptionList, - subscriptionCount, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Make sure buffer has enough space */ - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); - - /* Make sure subscription count of zero fails. */ - status = MQTT_SerializeUnsubscribe( &subscriptionList, - 0, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - /* Test if buffer is too small. */ - fixedBuffer.size = 1; - status = MQTT_SerializeUnsubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); - fixedBuffer.size = bufferSize; - - /* Make sure success it returned for good case. */ - padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeUnsubscribe( &subscriptionList, - subscriptionCount, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - checkBufferOverflow( buffer, sizeof( buffer ) ); - - /* MQTT UNSUBSCRIBE packet format: - * 0xA2 (1 byte) - * Remaining length (1-4 bytes) - * Packet ID (2 bytes) - * Topic filters (series of 2 byte lengths followed by filter) (variable) */ - *pIterator++ = MQTT_PACKET_TYPE_UNSUBSCRIBE; - pIterator += encodeRemainingLength( pIterator, remainingLength ); - *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); - *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); - pIterator += encodeString( pIterator, subscriptionList.pTopicFilter, subscriptionList.topicFilterLength ); - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); -} - -/* ========================================================================== */ - -/** - * @brief Tests that MQTT_GetPublishPacketSize works as intended. - */ -void test_MQTT_GetPublishPacketSize( void ) -{ - MQTTPublishInfo_t publishInfo; - size_t remainingLength = 0; - size_t packetSize; - MQTTStatus_t status = MQTTSuccess; - - /* Verify bad parameters fail. */ - status = MQTT_GetPublishPacketSize( NULL, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - - status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &packetSize ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, NULL ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - - /* Empty topic must fail. */ - memset( &publishInfo, 0x00, sizeof( publishInfo ) ); - publishInfo.pTopicName = NULL; - publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - - publishInfo.pTopicName = TEST_TOPIC_NAME; - publishInfo.topicNameLength = 0; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - - /* Packet too large. */ - memset( &publishInfo, 0x00, sizeof( publishInfo ) ); - publishInfo.pTopicName = "/test/topic"; - publishInfo.topicNameLength = sizeof( "/test/topic" ); - publishInfo.payloadLength = MQTT_MAX_REMAINING_LENGTH; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - - publishInfo.payloadLength = MQTT_MAX_REMAINING_LENGTH - publishInfo.topicNameLength - sizeof( uint16_t ) - 1; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); - - /* Good case succeeds. */ - publishInfo.pTopicName = "/test/topic"; - publishInfo.topicNameLength = sizeof( "/test/topic" ); - publishInfo.pPayload = ""; - publishInfo.payloadLength = 0; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, status ); - - /* Again with QoS 2. */ - publishInfo.qos = MQTTQoS2; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, status ); -} - -/** - * @brief Tests that MQTT_SerializePublish works as intended. - */ -void test_MQTT_SerializePublish( void ) -{ - MQTTPublishInfo_t publishInfo; - size_t remainingLength = 98; - uint8_t buffer[ 200 + 2 * BUFFER_PADDING_LENGTH ]; - size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH; - size_t packetSize = bufferSize; - MQTTStatus_t status = MQTTSuccess; - MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; - uint8_t expectedPacket[ 200 ]; - uint8_t * pIterator; - - const uint16_t PACKET_ID = 1; - const char * longTopic = "/test/topic/name/longer/than/one/hundred/twenty/eight/characters" \ - "/test/topic/name/longer/than/one/hundred/twenty/eight/characters"; - - /* Verify bad parameters fail. */ - memset( &publishInfo, 0x00, sizeof( publishInfo ) ); - publishInfo.pTopicName = "/test/topic"; - publishInfo.topicNameLength = sizeof( "/test/topic" ); - - status = MQTT_SerializePublish( NULL, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - status = MQTT_SerializePublish( &publishInfo, - PACKET_ID, - remainingLength, - NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - /* Verify a NULL buffer in the fixed buffer struct fails */ - fixedBuffer.pBuffer = NULL; - status = MQTT_SerializePublish( &publishInfo, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - - /* Restore the fixed buffer. */ - fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; - - /* Verify that a non-zero payload length and a NULL payload fails. */ - publishInfo.payloadLength = 1; - publishInfo.pPayload = NULL; - status = MQTT_SerializePublish( &publishInfo, - PACKET_ID, - remainingLength, - &fixedBuffer ); + /* Verify that a non-zero payload length and a NULL payload fails. */ + publishInfo.payloadLength = 1; + publishInfo.pPayload = NULL; + status = MQTT_SerializePublish( &publishInfo, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Restore the payload length to valid for tests. */ @@ -1183,6 +1713,7 @@ void test_MQTT_SerializePublish( void ) /* Verify that 0 packet ID for QoS > 0 fails. */ publishInfo.qos = MQTTQoS1; status = MQTT_SerializePublish( &publishInfo, + NULL, 0, remainingLength, &fixedBuffer ); @@ -1192,6 +1723,7 @@ void test_MQTT_SerializePublish( void ) publishInfo.qos = MQTTQoS0; publishInfo.dup = true; status = MQTT_SerializePublish( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer ); @@ -1205,6 +1737,7 @@ void test_MQTT_SerializePublish( void ) publishInfo.pTopicName = NULL; publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH; status = MQTT_SerializePublish( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer ); @@ -1213,6 +1746,7 @@ void test_MQTT_SerializePublish( void ) publishInfo.pTopicName = TEST_TOPIC_NAME; publishInfo.topicNameLength = 0; status = MQTT_SerializePublish( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer ); @@ -1223,6 +1757,7 @@ void test_MQTT_SerializePublish( void ) publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH; fixedBuffer.size = 5; status = MQTT_SerializePublish( &publishInfo, + NULL, PACKET_ID, 10, &fixedBuffer ); @@ -1234,13 +1769,14 @@ void test_MQTT_SerializePublish( void ) publishInfo.topicNameLength = sizeof( "/test/topic" ); fixedBuffer.size = bufferSize; /* Calculate exact packet size and remaining length. */ - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); /* Make sure buffer has enough space */ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); padAndResetBuffer( buffer, sizeof( buffer ) ); status = MQTT_SerializePublish( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer ); @@ -1253,10 +1789,12 @@ void test_MQTT_SerializePublish( void ) * Topic name length (2 bytes) * Topic name (variable) * Packet ID (if QoS > 0) (1 byte) + * Properties length (1-4 bytes) - 1 in this case * Payload (>= 0 bytes) */ expectedPacket[ 0 ] = MQTT_PACKET_TYPE_PUBLISH; expectedPacket[ 1 ] = remainingLength; ( void ) encodeString( &expectedPacket[ 2 ], publishInfo.pTopicName, publishInfo.topicNameLength ); + expectedPacket[ 16 ] = 0; TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); /* Again with QoS2, dup, and retain. Also encode remaining length > 2 bytes. */ @@ -1269,12 +1807,13 @@ void test_MQTT_SerializePublish( void ) publishInfo.payloadLength = MQTT_SAMPLE_PAYLOAD_LEN; memset( buffer, 0x00, bufferSize ); /* Calculate exact packet size and remaining length. */ - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); /* Make sure buffer has enough space */ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); padAndResetBuffer( buffer, sizeof( buffer ) ); status = MQTT_SerializePublish( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer ); @@ -1288,544 +1827,1484 @@ void test_MQTT_SerializePublish( void ) pIterator += encodeString( pIterator, publishInfo.pTopicName, publishInfo.topicNameLength ); *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); + *pIterator++ = 0; ( void ) memcpy( pIterator, publishInfo.pPayload, publishInfo.payloadLength ); TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); } /* ========================================================================== */ -/** - * @brief Tests that MQTT_GetDisconnectPacketSize works as intended. - */ -void test_MQTT_GetDisconnectPacketSize( void ) +void test_MQTTV5_DeserializeAck_puback( void ) { - MQTTStatus_t status; - size_t packetSize; + MQTTPacketInfo_t mqttPacketInfo; + MQTTReasonCodeInfo_t ackInfo; + MQTTPropBuilder_t propBuffer = { 0 }; + uint16_t packetIdentifier; + uint32_t maxPacketSize = 0U; + bool requestProblem = false; + MQTTStatus_t status = MQTTSuccess; + uint8_t buffer[ 100 ] = { 0 }; + uint8_t * pIndex = buffer; + size_t dummy; - /* Verify parameters. */ - status = MQTT_GetDisconnectPacketSize( NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + /* Verify parameters */ + memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); + memset( &ackInfo, 0x00, sizeof( ackInfo ) ); - /* Good case succeeds. A DISCONNECT is 2 bytes. */ - status = MQTT_GetDisconnectPacketSize( &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( 2, packetSize ); -} -/** - * @brief Tests that MQTT_SerializeDisconnect works as intended. + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBACK; + status = MQTT_DeserializeAck( NULL, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Remaining data cannot be NULL.*/ + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Max packet size cannot be 0*/ + mqttPacketInfo.pRemainingData = buffer; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, NULL, requestProblem, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + maxPacketSize = 200U; + /* Packet identifier 0 is not valid (per spec). */ + buffer[ 0 ] = 0; + buffer[ 1 ] = 0; + mqttPacketInfo.remainingLength = MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + + /*Remaining length connot be less than 2*/ + mqttPacketInfo.remainingLength = 1; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Packet size greater than allowed.*/ + mqttPacketInfo.remainingLength = 1000U; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /* Process a valid PUBACK. */ + mqttPacketInfo.remainingLength = 2; + buffer[ 1 ] = 1; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + TEST_ASSERT_EQUAL_INT( 1, packetIdentifier ); + + mqttPacketInfo.remainingLength = 3; + buffer[ 2 ] = 0x00; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + TEST_ASSERT_EQUAL_INT( 1, packetIdentifier ); + + /*Property length should be zero when request problem is set to false*/ + mqttPacketInfo.remainingLength = 24; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + requestProblem = true; + /*User properties not initialized.*/ + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Valid parameters.*/ + pIndex = &buffer[ 3 ]; + dummy = encodeRemainingLength( pIndex, 20 ); + pIndex++; + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + pIndex = serializeutf_8pair( pIndex ); + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*With NULL prop builder. */ + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Invalid property id*/ + pIndex = &buffer[ 3 ]; + dummy = encodeRemainingLength( pIndex, 7 ); + mqttPacketInfo.remainingLength = dummy + 7 + 3; + pIndex++; + pIndex = serializeutf_8( pIndex, MQTT_CORRELATION_DATA_ID ); + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid remaining length*/ + pIndex = &buffer[ 3 ]; + dummy = encodeRemainingLength( pIndex, 12 ); + pIndex++; + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + + /*Invalid property length*/ + pIndex = &buffer[ 3 ]; + dummy = encodeRemainingLength( pIndex, 20971556356235 ); + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} + +void test_MQTTV5_DeserializeAck_LogPuback() +{ + MQTTPacketInfo_t mqttPacketInfo; + MQTTReasonCodeInfo_t ackInfo; + uint16_t packetIdentifier; + uint32_t maxPacketSize = 10U; + bool requestProblem = false; + MQTTStatus_t status = MQTTSuccess; + uint8_t buffer[ 4 ] = { 0 }; + + memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); + memset( &ackInfo, 0x00, sizeof( ackInfo ) ); + + MQTTPropBuilder_t propBuffer = { 0 }; + mqttPacketInfo.pRemainingData = buffer; + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBACK; + mqttPacketInfo.remainingLength = 4; + /*Validate all the correct reason codes.*/ + buffer[ 1 ] = 1; + buffer[ 2 ] = MQTT_REASON_SUCCESS; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + buffer[ 2 ] = MQTT_REASON_NO_MATCHING_SUBSCRIBERS; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + buffer[ 2 ] = MQTT_REASON_UNSPECIFIED_ERR; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + buffer[ 2 ] = MQTT_REASON_IMPL_SPECIFIC_ERR; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + buffer[ 2 ] = MQTT_REASON_NOT_AUTHORIZED; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + buffer[ 2 ] = MQTT_REASON_TOPIC_NAME_INVALID; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + buffer[ 2 ] = MQTT_REASON_PACKET_ID_IN_USE; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + buffer[ 2 ] = MQTT_REASON_QUOTA_EXCEEDED; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + buffer[ 2 ] = MQTT_REASON_PAYLOAD_FORMAT_INVALID; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + /*Invlaid reason code.*/ + buffer[ 2 ] = MQTT_REASON_BANNED; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} + +void test_MQTTV5_DeserializeAck_Pubrel() +{ + MQTTPacketInfo_t mqttPacketInfo; + MQTTReasonCodeInfo_t ackInfo; + uint32_t maxPacketSize = 10U; + uint16_t packetIdentifier; + bool requestProblem = false; + MQTTStatus_t status = MQTTSuccess; + MQTTPropBuilder_t propBuffer; + uint8_t buffer[ 4 ] = { 0 }; + + memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); + memset( &ackInfo, 0x00, sizeof( ackInfo ) ); + + mqttPacketInfo.pRemainingData = buffer; + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBREL; + mqttPacketInfo.remainingLength = 4; + /*Validate all the correct reason codes.*/ + buffer[ 1 ] = 1; + buffer[ 2 ] = MQTT_REASON_SUCCESS; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + buffer[ 2 ] = MQTT_REASON_PACKET_ID_NOT_FOUND; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + + /*Invalid reason code.*/ + buffer[ 2 ] = MQTT_REASON_BANNED; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid reason code.*/ + buffer[ 2 ] = MQTT_REASON_SEND_WILL; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid packet id*/ + buffer[ 1 ] = 0; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid packet type. */ + mqttPacketInfo.type = MQTT_PACKET_TYPE_DISCONNECT; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBREL; + buffer[ 1 ] = 1; /* Reset packet identifier to a valid value. */ + mqttPacketInfo.remainingLength = 2; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &ackInfo, requestProblem, maxPacketSize, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + + +void test_MQTTV5_GetAckPacketSize() +{ + MQTTStatus_t status; + size_t remainingLength; + size_t packetSize; + uint32_t maxPacketSize = 0U; + + /*Invalid parameters*/ + status = MQTT_GetAckPacketSize( &remainingLength, &packetSize, maxPacketSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_GetAckPacketSize( NULL, &packetSize, maxPacketSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_GetAckPacketSize( &remainingLength, NULL, maxPacketSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Max packet size cannot be 0*/ + status = MQTT_GetAckPacketSize( &remainingLength, NULL, maxPacketSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Valid parameters*/ + maxPacketSize = UINT32_MAX; + status = MQTT_GetAckPacketSize( &remainingLength, &packetSize, maxPacketSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*With properties*/ + status = MQTT_GetAckPacketSize( &remainingLength, &packetSize, maxPacketSize, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Packet size greater than max allowed.*/ + maxPacketSize = 2; + status = MQTT_GetAckPacketSize( &remainingLength, &packetSize, maxPacketSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Max packet size cannot be 0*/ + maxPacketSize = 0; + status = MQTT_GetAckPacketSize( &remainingLength, &packetSize, maxPacketSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + maxPacketSize = 500; + status = MQTT_GetAckPacketSize( &remainingLength, &packetSize, maxPacketSize, MQTT_MAX_REMAINING_LENGTH ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + + +void test_MQTTV5_GetDisconnectPacketSize() +{ + size_t remainingLength; + size_t packetSize; + uint32_t maxPacketSize = 0U; + MQTTStatus_t status; + + /*Invalid arguments*/ + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + + status = MQTT_GetDisconnectPacketSize( NULL, NULL, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, NULL, maxPacketSize, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Max packet size cannot be 0.*/ + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* / *Invalid Reason code* / */ + maxPacketSize = 60U; + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_SUBACK_GRANTED_QOS1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Valid parameters*/ + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_DISCONNECT_WITH_WILL_MESSAGE ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Valid parameters*/ + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_PACKET_TOO_LARGE ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_SERVER_BUSY ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Max packet size lesser than packet size */ + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); + propBuffer.currentIndex = 10; + status = MQTT_GetDisconnectPacketSize( &propBuffer, &remainingLength, &packetSize, 6, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + + propBuffer.currentIndex = MQTT_MAX_REMAINING_LENGTH; /* Other fields do not have to be set as we only testing if the length of properties == max_remaining_length */ + status = MQTT_GetDisconnectPacketSize( &propBuffer, &remainingLength, &packetSize, maxPacketSize, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + propBuffer.pBuffer = NULL; + status = MQTT_GetDisconnectPacketSize( &propBuffer, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + + + +void test_MQTTV5_DeserializeDisconnect() +{ + MQTTReasonCodeInfo_t disconnectInfo; + size_t dummy; + int32_t maxPacketSize = 0U; + uint8_t buffer[ 100 ] = { 0 }; + uint8_t * pIndex = buffer; + MQTTPropBuilder_t propBuffer = { 0 }; + + memset( &disconnectInfo, 0x0, sizeof( disconnectInfo ) ); + /*Invalid parameters*/ + status = MQTT_DeserializeDisconnect( NULL, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Remaining data not initialized.*/ + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + packetInfo.pRemainingData = buffer; + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, NULL, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /*Max packet size cannot be 0.*/ + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + maxPacketSize = 100; + + /*Remaining length can be 0*/ + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Remaining Length invalid. */ + packetInfo.remainingLength = 200; + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + + /*Invalid reason code.*/ + buffer[ 0 ] = MQTT_REASON_DISCONNECT_DISCONNECT_WITH_WILL_MESSAGE; + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + packetInfo.remainingLength = 1; + buffer[ 0 ] = MQTT_REASON_DISCONNECT_NOT_AUTHORIZED; + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Property length is 0.*/ + packetInfo.remainingLength = 2; + pIndex = &buffer[ 1 ]; + dummy = encodeRemainingLength( pIndex, 0 ); + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*With properties*/ + buffer[ 0 ] = MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION; + pIndex = &buffer[ 1 ]; + packetInfo.remainingLength = 29; + dummy = encodeRemainingLength( pIndex, 27 ); + pIndex++; + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + pIndex = serializeutf_8pair( pIndex ); + pIndex = serializeutf_8( pIndex, MQTT_SERVER_REF_ID ); + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + + + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); + + buffer[ 0 ] = MQTT_REASON_DISCONNECT_DISCONNECT_WITH_WILL_MESSAGE; + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + buffer[ 0 ] = MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION; + /*Invalid property id.*/ + pIndex = &buffer[ 1 ]; + packetInfo.remainingLength = 9; + dummy = encodeRemainingLength( pIndex, 7 ); + pIndex++; + pIndex = serializeutf_8( pIndex, MQTT_SESSION_EXPIRY_ID ); + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + TEST_ASSERT_EQUAL_INT( 1, dummy ); + + /*Invalid property length.*/ + pIndex = &buffer[ 1 ]; + packetInfo.remainingLength = 9; + dummy = encodeRemainingLength( pIndex, 4 ); + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + buffer[ 1 ] = 0x81; + buffer[ 2 ] = 0x00; + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, &propBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + buffer[ 0 ] = MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION; + pIndex = &buffer[ 1 ]; + packetInfo.remainingLength = 2; + dummy = encodeRemainingLength( pIndex, 0 ); + status = MQTT_DeserializeDisconnect( &packetInfo, maxPacketSize, &disconnectInfo, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + +void test_MQTT_GetIncomingPacketTypeAndLength( void ) +{ + MQTTPacketInfo_t mqttPacket; + NetworkContext_t networkContext; + uint8_t buffer[ 10 ]; + uint8_t * bufPtr = buffer; + + /* Dummy network context - pointer to pointer to a buffer. */ + networkContext.buffer = &bufPtr; + /* Check when network receive fails. */ + memset( buffer, 0x00, 10 ); + /* Branch coverage for Disconnect. */ + bufPtr = buffer; + buffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT; + status = MQTT_GetIncomingPacketTypeAndLength( mockReceive, &networkContext, &mqttPacket ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); +} + +void test_MQTTV5_GetSubscribePacketSize( void ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTSubscribeInfo_t subscribeInfo; + size_t remainingLength = 0; + size_t packetSize = 0; + + /** Verify Parameters */ + + /** NULL parameters */ + status = MQTT_GetSubscribePacketSize( NULL, 1, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_GetSubscribePacketSize( &subscribeInfo, 0, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + subscribeInfo.topicFilterLength = 13; + subscribeInfo.pTopicFilter = "example/topic"; + /*Invalid max packet size*/ + status = MQTT_GetSubscribePacketSize( &subscribeInfo, 1, NULL, &remainingLength, &packetSize, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_GetSubscribePacketSize( &subscribeInfo, 1, NULL, &remainingLength, &packetSize, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +void test_MQTTV5_GetSubscribePacketSize_HappyPath( void ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTSubscribeInfo_t subscribeInfo; + size_t remainingLength = 0; + size_t packetSize = 0; + + subscribeInfo.pTopicFilter = TEST_TOPIC_NAME; + subscribeInfo.topicFilterLength = TEST_TOPIC_NAME_LENGTH; + subscribeInfo.qos = MQTTQoS0; + subscribeInfo.noLocalOption = 0; + subscribeInfo.retainAsPublishedOption = 0; + subscribeInfo.retainHandlingOption = 1; + + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 50 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = 50; + propBuffer.currentIndex = 2; + + status = MQTT_GetSubscribePacketSize( &subscribeInfo, 1, &propBuffer, &remainingLength, &packetSize, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + TEST_ASSERT_EQUAL_UINT32( 19U, remainingLength ); + TEST_ASSERT_EQUAL_UINT32( 21U, packetSize ); + + /* Test function with null buffer. */ + propBuffer.pBuffer = NULL; + status = MQTT_GetSubscribePacketSize( &subscribeInfo, 1, &propBuffer, &remainingLength, &packetSize, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + +/** Subscribe Packet size with multiple subscriptions and User Properties */ +void test_MQTTV5_GetSubscribePacketSize_MultipleSubscriptions( void ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTSubscribeInfo_t subscribeInfo[ 2 ]; + size_t remainingLength = 0; + size_t packetSize = 0; + + subscribeInfo[ 0 ].pTopicFilter = TEST_TOPIC_NAME; + subscribeInfo[ 0 ].topicFilterLength = TEST_TOPIC_NAME_LENGTH; + subscribeInfo[ 0 ].qos = MQTTQoS0; + subscribeInfo[ 0 ].noLocalOption = 0; + subscribeInfo[ 0 ].retainAsPublishedOption = 0; + subscribeInfo[ 0 ].retainHandlingOption = 1; + + subscribeInfo[ 1 ].pTopicFilter = TEST_TOPIC_NAME; + subscribeInfo[ 1 ].topicFilterLength = TEST_TOPIC_NAME_LENGTH; + subscribeInfo[ 1 ].qos = MQTTQoS0; + subscribeInfo[ 1 ].noLocalOption = 0; + subscribeInfo[ 1 ].retainAsPublishedOption = 0; + subscribeInfo[ 1 ].retainHandlingOption = 1; + + status = MQTT_GetSubscribePacketSize( subscribeInfo, 2, NULL, &remainingLength, &packetSize, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + +void test_calculateSubscriptionPacketSizeV5( void ) +{ + size_t subscriptionCount = 1; + size_t remainingLength = 0; + size_t packetSize = 0; + MQTTStatus_t status = MQTTSuccess; + MQTTSubscribeInfo_t fourThousandSubscriptions[ 4096 ] = { 0 }; + int i; + + for( i = 0; i < 4096; i++ ) + { + fourThousandSubscriptions[ i ].topicFilterLength = UINT16_MAX; + + /* We need to set this to avoid an early bad parameter, however we do + * not need a 65535 byte buffer as the packet will not be serialized. */ + fourThousandSubscriptions[ i ].pTopicFilter = ""; + } + + subscriptionCount = sizeof( fourThousandSubscriptions ) / sizeof( fourThousandSubscriptions[ 0 ] ); + status = MQTT_GetSubscribePacketSize( fourThousandSubscriptions, + subscriptionCount, + NULL, + &remainingLength, + &packetSize, MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +/** + * @brief Tests that MQTT_SerializeSubscribe works as intended. */ -void test_MQTT_SerializeDisconnect( void ) +void test_MQTT_SerializeSubscribe( void ) { - uint8_t buffer[ 10 + 2 * BUFFER_PADDING_LENGTH ]; - MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ] }; - uint8_t expectedPacket[ 2 ] = { MQTT_PACKET_TYPE_DISCONNECT, 0 }; + MQTTSubscribeInfo_t subscriptionList; + size_t subscriptionCount = 1; + size_t remainingLength = 0; + uint8_t buffer[ 25 + 2 * BUFFER_PADDING_LENGTH ]; + size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH; + size_t packetSize = bufferSize; MQTTStatus_t status = MQTTSuccess; + MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; + uint8_t expectedPacket[ 100 ]; + uint8_t * pIterator = expectedPacket; + + const uint16_t PACKET_ID = 1; + + /* Verify bad parameters fail. */ + status = MQTT_SerializeSubscribe( NULL, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + NULL, + 0, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* Verify a NULL buffer in the fixed buffer struct fails */ + fixedBuffer.pBuffer = NULL; + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* Restore the fixed buffer. */ + fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; - /* Buffer size less than disconnect request fails. */ + /* Get correct values of packet size and remaining length. */ + memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); + subscriptionList.qos = MQTTQoS0; + subscriptionList.pTopicFilter = "/example/topic"; + subscriptionList.topicFilterLength = sizeof( "/example/topic" ); + subscriptionList.noLocalOption = 1; + subscriptionList.retainAsPublishedOption = 1; + subscriptionList.retainHandlingOption = 1; + uint32_t maxPacketSize = MQTT_MAX_PACKET_SIZE; + subscriptionCount = sizeof( subscriptionList ) / sizeof( MQTTSubscribeInfo_t ); + status = MQTT_GetSubscribePacketSize( &subscriptionList, + subscriptionCount, + NULL, + &remainingLength, + &packetSize, + maxPacketSize ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* Make sure buffer has enough space */ + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + + /* Make sure subscription count of zero fails. */ + status = MQTT_SerializeSubscribe( &subscriptionList, + 0, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* Test if buffer is too small. */ fixedBuffer.size = 1; - status = MQTT_SerializeDisconnect( &fixedBuffer ); + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); + fixedBuffer.size = bufferSize; - /* NULL buffer fails. */ - status = MQTT_SerializeDisconnect( NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + /* Make sure success is returned for good case. */ + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + + /* MQTT SUBSCRIBE packet format: + * 0x82 (1 byte) + * Remaining length (1-4 bytes) + * Packet ID (2 bytes) + * Topic filters (series of 2 byte lengths followed by filter, then QoS) (variable) */ + /* uint8_t subscriptionOptions = 0 ; */ + /* subscriptionOptions = subscriptionOptions & */ + *pIterator++ = MQTT_PACKET_TYPE_SUBSCRIBE; + pIterator += encodeRemainingLength( pIterator, remainingLength ); + *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); + *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); + *pIterator++ = 0; /* Length of properties = 0 */ + pIterator += encodeString( pIterator, subscriptionList.pTopicFilter, subscriptionList.topicFilterLength ); + *pIterator++ = 0x1C; + TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); - /* Good case succeeds. */ - fixedBuffer.size = 2; padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeDisconnect( &fixedBuffer ); + subscriptionList.noLocalOption = 0; + subscriptionList.retainAsPublishedOption = 0; + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], 2 ); -} -/** - * @brief Tests that MQTT_GetPingreqPacketSize works as intended. - */ -void test_MQTT_GetPingreqPacketSize( void ) -{ - MQTTStatus_t status; - size_t packetSize; + pIterator = expectedPacket; + *pIterator++ = MQTT_PACKET_TYPE_SUBSCRIBE; + pIterator += encodeRemainingLength( pIterator, remainingLength ); + *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); + *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); + *pIterator++ = 0; /* Length of properties = 0 */ + pIterator += encodeString( pIterator, subscriptionList.pTopicFilter, subscriptionList.topicFilterLength ); + *pIterator++ = 0x10; + TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); - /* Verify parameters. */ - status = MQTT_GetPingreqPacketSize( NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + /* Serialize subscribe with properties */ + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); - /* Good case succeeds. A PINGREQ is 2 bytes. */ - status = MQTT_GetPingreqPacketSize( &packetSize ); + MQTTPropAdd_SubscribeId( &propBuffer, 10 ); + status = MQTT_GetSubscribePacketSize( &subscriptionList, + subscriptionCount, + &propBuffer, + &remainingLength, + &packetSize, + maxPacketSize ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* Make sure buffer has enough space */ + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + + fixedBuffer.size = bufferSize; + + /* Make sure success is returned for good case. */ + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + + /*test SerializeSubscribe with NULL property buffer. */ + propBuffer.pBuffer = NULL; + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer ); + + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + + /*test MQTT_SerializeSubscribe subscription options. */ + subscriptionList.qos = MQTTQoS1; + subscriptionList.pTopicFilter = "/example/topic"; + subscriptionList.topicFilterLength = sizeof( "/example/topic" ); + subscriptionList.noLocalOption = 1; + subscriptionList.retainAsPublishedOption = 1; + subscriptionList.retainHandlingOption = retainSendOnSub; + + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + subscriptionList.qos = MQTTQoS2; + subscriptionList.retainHandlingOption = retainDoNotSendonSub; + + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializeSubscribe( &subscriptionList, + subscriptionCount, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( 2, packetSize ); } /** - * @brief Tests that MQTT_SerializePingreq works as intended. + * @brief Tests that MQTT_SerializeUnsubscribe works as intended. */ -void test_MQTT_SerializePingreq( void ) +void test_MQTT_SerializeUnsubscribe( void ) { - uint8_t buffer[ 10 + 2 * BUFFER_PADDING_LENGTH ]; - MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ] }; - uint8_t expectedPacket[ 2 ] = { MQTT_PACKET_TYPE_PINGREQ, 0 }; + MQTTSubscribeInfo_t subscriptionList; + size_t subscriptionCount = 1; + size_t remainingLength = 0; + uint8_t buffer[ 33 + 2 * BUFFER_PADDING_LENGTH ]; + size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH; + size_t packetSize = bufferSize; MQTTStatus_t status = MQTTSuccess; + MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; + uint8_t expectedPacket[ 100 ]; + uint8_t * pIterator = expectedPacket; - /* Buffer size less than ping request fails. */ - fixedBuffer.size = 1; - status = MQTT_SerializePingreq( &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); + const uint16_t PACKET_ID = 1; - /* NULL buffer fails. */ - status = MQTT_SerializePingreq( NULL ); + status = MQTT_SerializeUnsubscribe( NULL, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_SerializeUnsubscribe( &subscriptionList, + subscriptionCount, + NULL, + 0, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_SerializeUnsubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Verify a NULL buffer in the fixed buffer struct fails */ fixedBuffer.pBuffer = NULL; - status = MQTT_SerializePingreq( &fixedBuffer ); + status = MQTT_SerializeUnsubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); /* Restore the fixed buffer. */ fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; - /* Good case succeeds. */ - fixedBuffer.size = 2; + /* Get correct values of packetsize and remaining length. */ + memset( &subscriptionList, 0x0, sizeof( subscriptionList ) ); + subscriptionList.qos = MQTTQoS0; + subscriptionList.pTopicFilter = "/example/topic"; + subscriptionList.topicFilterLength = sizeof( "/example/topic" ); + subscriptionCount = sizeof( subscriptionList ) / sizeof( MQTTSubscribeInfo_t ); + status = MQTT_GetUnsubscribePacketSize( &subscriptionList, + subscriptionCount, + NULL, + &remainingLength, + &packetSize, + MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* Make sure buffer has enough space */ + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + + /* Make sure subscription count of zero fails. */ + status = MQTT_SerializeUnsubscribe( &subscriptionList, + 0, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* Test if buffer is too small. */ + fixedBuffer.size = 1; + status = MQTT_SerializeUnsubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); + fixedBuffer.size = bufferSize; + + /* Make sure success it returned for good case. */ padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializePingreq( &fixedBuffer ); + status = MQTT_SerializeUnsubscribe( &subscriptionList, + subscriptionCount, + NULL, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + + /* MQTT UNSUBSCRIBE packet format: + * 0xA2 (1 byte) + * Remaining length (1-4 bytes) + * Packet ID (2 bytes) + * Topic filters (series of 2 byte lengths followed by filter) (variable) */ + *pIterator++ = MQTT_PACKET_TYPE_UNSUBSCRIBE; + pIterator += encodeRemainingLength( pIterator, remainingLength ); + *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); + *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); + *pIterator++ = 0; + pIterator += encodeString( pIterator, subscriptionList.pTopicFilter, subscriptionList.topicFilterLength ); + TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); + + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 50 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); + + MQTTUserProperty_t userProp; + userProp.pKey = "abc"; + userProp.pValue = "def"; + userProp.keyLength = 3; + userProp.valueLength = 3; + MQTTPropAdd_UserProp( &propBuffer, &userProp ); + + status = MQTT_GetUnsubscribePacketSize( &subscriptionList, + subscriptionCount, + &propBuffer, + &remainingLength, + &packetSize, + MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* Make sure buffer has enough space */ + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializeUnsubscribe( &subscriptionList, + subscriptionCount, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + + /*Test with null property buffer. */ + propBuffer.pBuffer = NULL; + status = MQTT_SerializeUnsubscribe( &subscriptionList, + subscriptionCount, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); checkBufferOverflow( buffer, sizeof( buffer ) ); - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], 2 ); } - /* ========================================================================== */ -/** - * @brief Tests that MQTT_DeserializeAck works as intended with a CONNACK. - */ -void test_MQTT_DeserializeAck_connack( void ) +void test_MQTTV5_suback( void ) { - MQTTPacketInfo_t mqttPacketInfo; - uint16_t packetIdentifier; - bool sessionPresent = true; - MQTTStatus_t status = MQTTSuccess; - uint8_t buffer[ 10 ]; + MQTTStatus_t status; + uint8_t * pIndex; - /* Verify parameters */ - memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); - mqttPacketInfo.type = MQTT_PACKET_TYPE_CONNACK; - status = MQTT_DeserializeAck( NULL, &packetIdentifier, &sessionPresent ); + uint8_t packetBuffer[ 23 ] = + { + 0x90, /* Fixed header: SUBACK type (0x90) */ + 0x14, /* Remaining Length = 20 bytes */ + 0x00, 0x01, /* Packet Identifier = 1 */ + 0x11, /* Property Length = 17 bytes */ + 0x1F, /* Property ID = 0x1F (Reason String) */ + 0x00, 0x03, /* UTF-8 string length = 3 */ + 0x61, 0x62, 0x63, /* The string "abc" */ + 0x26, 0x00, 0x03, 0x61, 0x62, 0x63, 0x00, 0x03, 0x61, 0x62, 0x63, + 0x00 /* Payload: Reason code = 0x00 (Success) */ + }; + + MQTTPacketInfo_t subackPacket; + + memset( &subackPacket, 0, sizeof( subackPacket ) ); + subackPacket.type = MQTT_PACKET_TYPE_SUBACK; /* Should be defined as 0x90 */ + subackPacket.remainingLength = 21; /* From the fixed header (0x0A) */ + subackPacket.headerLength = 2; /* Fixed header size in this example */ + subackPacket.pRemainingData = &packetBuffer[ 2 ]; + uint16_t packetIdentifier = 0; + MQTTReasonCodeInfo_t subackReasonCodes; + MQTTPropBuilder_t propBuffer = { 0 }; + status = MQTT_DeserializeAck( &subackPacket, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + packetBuffer[ 11 ] = 0x00; + status = MQTT_DeserializeAck( &subackPacket, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + status = MQTT_DeserializeAck( &subackPacket, &packetIdentifier, NULL, NULL, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Packet ID can be NULL for CONNACK, don't need to check that. */ - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL ); + + status = MQTT_DeserializeAck( NULL, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + status = MQTT_DeserializeAck( &subackPacket, NULL, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Bad packet type. */ - mqttPacketInfo.type = 0x01; - mqttPacketInfo.pRemainingData = buffer; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + uint8_t packetBufferNoProperties[ 7 ] = + { + 0x90, /* Fixed header: SUBACK type (0x90) */ + 4, /* Remaining Length = 4 bytes */ + 0x00, 0x01, /* Packet Identifier = 1 */ + 0x00, /* Property Length = 1 byte */ + 0x00, /* Payload: Reason code = 0x00 (Success) */ + 0x00 + }; + + memset( &subackPacket, 0, sizeof( subackPacket ) ); + subackPacket.type = MQTT_PACKET_TYPE_SUBACK; /* Should be defined as 0x90 */ + subackPacket.remainingLength = 4; + subackPacket.headerLength = 2; + subackPacket.pRemainingData = &packetBufferNoProperties[ 2 ]; + packetIdentifier = 0; + status = MQTT_DeserializeAck( &subackPacket, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Bad remaining length. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_CONNACK; - mqttPacketInfo.remainingLength = MQTT_PACKET_CONNACK_REMAINING_LENGTH - 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + packetBufferNoProperties[ 5 ] = 0x80; /* Change reason code to 0x01 (Unspecified error) */ + status = MQTT_DeserializeAck( &subackPacket, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); - /* Incorrect reserved bits. */ - mqttPacketInfo.remainingLength = MQTT_PACKET_CONNACK_REMAINING_LENGTH; - buffer[ 0 ] = 0xf; - buffer[ 1 ] = 0; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + /*Invalid Property Length. */ + subackPacket.remainingLength = 20; + pIndex = &packetBufferNoProperties[ 4 ]; + encodeRemainingLength( pIndex, 20971556356235 ); + status = MQTT_DeserializeAck( &subackPacket, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Session present but nonzero return code. */ - buffer[ 0 ] = MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK; - buffer[ 1 ] = 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + subackPacket.remainingLength = 5; + packetBufferNoProperties[ 4 ] = 1; /* Set property length to 1 byte */ + packetBufferNoProperties[ 5 ] = 0x00; + packetBufferNoProperties[ 6 ] = 0x00; /* Set reason code to 0x00 (Success) */ + status = MQTT_DeserializeAck( &subackPacket, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} - /* Invalid response code. */ - buffer[ 0 ] = 0; - buffer[ 1 ] = 6; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +void test_MQTTV5_GetUnsubscribePacketSize_Path( void ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTSubscribeInfo_t subscribeInfo = { 0 }; + size_t remainingLength = 0; + size_t packetSize = 0; - /* Valid packet with rejected code. */ - buffer[ 1 ] = 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); + status = MQTT_GetUnsubscribePacketSize( NULL, 1, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Valid packet with success code when session present bit is set. */ - buffer[ 0 ] = MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK; - buffer[ 1 ] = 0; - sessionPresent = false; - status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, &sessionPresent ); + status = MQTT_GetUnsubscribePacketSize( &subscribeInfo, 1, NULL, NULL, &packetSize, MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + status = MQTT_GetUnsubscribePacketSize( &subscribeInfo, 1, NULL, &remainingLength, NULL, MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + subscribeInfo.pTopicFilter = TEST_TOPIC_NAME; + subscribeInfo.topicFilterLength = TEST_TOPIC_NAME_LENGTH; + + + status = MQTT_GetUnsubscribePacketSize( &subscribeInfo, 1, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( true, sessionPresent ); + TEST_ASSERT_EQUAL_UINT32( 16U, remainingLength ); + TEST_ASSERT_EQUAL_UINT32( 18U, packetSize ); - /* Valid packet with success code when session present bit is not set. */ - buffer[ 0 ] = 0; - status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, &sessionPresent ); + status = MQTT_GetUnsubscribePacketSize( &subscribeInfo, 0, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + MQTTPropBuilder_t propBuffer; + propBuffer.pBuffer = NULL; + status = MQTT_GetUnsubscribePacketSize( &subscribeInfo, 1, &propBuffer, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( false, sessionPresent ); } - -/** - * @brief Tests that MQTT_DeserializeAck works as intended with a SUBACK. - */ -void test_MQTT_DeserializeAck_suback( void ) +void test_MQTTV5_DeserializeSuback( void ) { MQTTPacketInfo_t mqttPacketInfo; uint16_t packetIdentifier; - bool sessionPresent; MQTTStatus_t status = MQTTSuccess; - uint8_t buffer[ 10 ] = { 0 }; + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buffer[ 50 ] = { 0 }; /* Bad remaining length. */ mqttPacketInfo.type = MQTT_PACKET_TYPE_SUBACK; mqttPacketInfo.pRemainingData = buffer; - mqttPacketInfo.remainingLength = 2; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - - /* Invalid packet ID. */ - buffer[ 0 ] = 0; - buffer[ 1 ] = 0; - mqttPacketInfo.remainingLength = 3; - buffer[ 2 ] = 0; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - + mqttPacketInfo.remainingLength = 14; /* Set packet identifier. */ buffer[ 0 ] = 0; buffer[ 1 ] = 1; - - /* Bad response code. */ - mqttPacketInfo.remainingLength = 3; - buffer[ 2 ] = 5; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - - /* Process a valid SUBACK with server refused response code. */ - mqttPacketInfo.remainingLength = 3; - buffer[ 2 ] = 0x80; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); - - /* Process a valid SUBACK with various server acceptance codes. */ - mqttPacketInfo.remainingLength = 5; - buffer[ 2 ] = 0x00; + buffer[ 2 ] = 0; buffer[ 3 ] = 0x01; buffer[ 4 ] = 0x02; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + buffer[ 5 ] = 0x80; + buffer[ 6 ] = 0x83; + buffer[ 7 ] = 0x87; + buffer[ 8 ] = 0x8F; + buffer[ 9 ] = 0x91; + buffer[ 10 ] = 0x97; + buffer[ 11 ] = 0x9E; + buffer[ 12 ] = 0xA1; + buffer[ 13 ] = 0xA2; + + MQTTReasonCodeInfo_t subackReasonCodes; + + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); -} -/** - * @brief Tests that MQTT_DeserializeAck works as intended with an UNSUBACK. - */ -void test_MQTT_DeserializeAck_unsuback( void ) -{ - MQTTPacketInfo_t mqttPacketInfo; - uint16_t packetIdentifier; - bool sessionPresent; - MQTTStatus_t status = MQTTSuccess; - uint8_t buffer[ 10 ] = { 0 }; + buffer[ 13 ] = 0xA4; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Bad remaining length. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_UNSUBACK; - mqttPacketInfo.pRemainingData = buffer; - mqttPacketInfo.remainingLength = MQTT_PACKET_UNSUBACK_REMAINING_LENGTH - 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + /*Max Packet Size lesser than suback packet size*/ + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &subackReasonCodes, 0, 1U, &propBuffer, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Packet identifier 0 is not valid (per spec). */ - buffer[ 0 ] = 0; + /*Invalid Remaining Length*/ + mqttPacketInfo.remainingLength = 2; + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + mqttPacketInfo.remainingLength = 14; + + /*Invalid packet type*/ buffer[ 1 ] = 0; - mqttPacketInfo.remainingLength = MQTT_PACKET_UNSUBACK_REMAINING_LENGTH; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Process a valid UNSUBACK. */ buffer[ 1 ] = 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + mqttPacketInfo.remainingLength = 17; + buffer[ 2 ] = 14; + uint8_t * pIndex = &buffer[ 3 ]; + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, &subackReasonCodes, 0, MQTT_MAX_PACKET_SIZE, &propBuffer, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); } -/** - * @brief Tests that MQTT_DeserializeAck works as intended with a PINGRESP. - */ -void test_MQTT_DeserializeAck_pingresp( void ) + +void test_incoming_publish1V5( void ) { MQTTPacketInfo_t mqttPacketInfo; - uint16_t packetIdentifier; - bool sessionPresent; + uint16_t packetIdentifier = 1; MQTTStatus_t status = MQTTSuccess; + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buffer[ 100 ] = { 0 }; + uint8_t * pIndex = NULL; - /* Bad remaining length. */ - ( void ) memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); - mqttPacketInfo.type = MQTT_PACKET_TYPE_PINGRESP; - mqttPacketInfo.remainingLength = MQTT_PACKET_PINGRESP_REMAINING_LENGTH + 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + buffer[ 0 ] = 0x00; + buffer[ 1 ] = 0x04; + buffer[ 2 ] = 't', buffer[ 3 ] = 'e', buffer[ 4 ] = 's', buffer[ 5 ] = 't'; + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; + mqttPacketInfo.pRemainingData = buffer; + pIndex = &buffer[ 6 ]; + + size_t propertyLength = encodeRemainingLength( pIndex, 46 ); + mqttPacketInfo.remainingLength = 52 + propertyLength; + pIndex++; + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + pIndex = serializeutf_8( pIndex, MQTT_RESPONSE_TOPIC_ID ); + pIndex = serializeutf_8( pIndex, MQTT_CORRELATION_DATA_ID ); + pIndex = serializeutf_8( pIndex, MQTT_CONTENT_TYPE_ID ); + pIndex = serializeutf_8pair( pIndex ); + *pIndex++ = MQTT_SUBSCRIPTION_ID_ID; + + MQTTPublishInfo_t publishIn; + ( void ) memset( &publishIn, 0x0, sizeof( publishIn ) ); + + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Test with NULL Property Builder. */ + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, NULL, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Invalid property length*/ + buffer[ 6 ] = 100; + mqttPacketInfo.remainingLength = 46; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Process a valid PINGRESP. */ - mqttPacketInfo.remainingLength = MQTT_PACKET_PINGRESP_REMAINING_LENGTH; - mqttPacketInfo.pRemainingData = NULL; - status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, NULL ); + /*Only packet ID present*/ + mqttPacketInfo.type = ( MQTT_PACKET_TYPE_PUBLISH | 0x04 ); + mqttPacketInfo.remainingLength = 9; + buffer[ 6 ] = 0x00, buffer[ 7 ] = 0x01, buffer[ 8 ] = 0x00; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); -} -/** - * @brief Tests that MQTT_DeserializeAck works as intended with a PUBACK, - * PUBREC, PUBREL, and PUBCOMP. - */ -void test_MQTT_DeserializeAck_puback( void ) -{ - MQTTPacketInfo_t mqttPacketInfo; - uint16_t packetIdentifier; - bool sessionPresent; - MQTTStatus_t status = MQTTSuccess; - uint8_t buffer[ 10 ] = { 0 }; + buffer[ 6 ] = 12; + pIndex = &buffer[ 7 ]; + mqttPacketInfo.remainingLength = 21; + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; + pIndex = serializeutf_8( pIndex, MQTT_RESPONSE_TOPIC_ID ); + pIndex = serializeutf_8( pIndex, MQTT_RESPONSE_TOPIC_ID ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Verify parameters */ - memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBACK; - status = MQTT_DeserializeAck( NULL, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* mqttPacketInfo.pRemainingData not set. */ - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + pIndex = &buffer[ 7 ]; + pIndex = serializeutf_8( pIndex, MQTT_CORRELATION_DATA_ID ); + pIndex = serializeutf_8( pIndex, MQTT_CORRELATION_DATA_ID ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Bad remaining length. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBACK; - mqttPacketInfo.pRemainingData = buffer; - mqttPacketInfo.remainingLength = MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH - 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + pIndex = &buffer[ 7 ]; + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Packet identifier 0 is not valid (per spec). */ - buffer[ 0 ] = 0; - buffer[ 1 ] = 0; - mqttPacketInfo.remainingLength = MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + pIndex = &buffer[ 7 ]; + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Process a valid PUBACK. */ - buffer[ 1 ] = 1; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( 1, packetIdentifier ); + pIndex = &buffer[ 7 ]; + buffer[ 6 ] = 10; + mqttPacketInfo.remainingLength = 17; + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* PUBREC. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBREC; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( 1, packetIdentifier ); + buffer[ 6 ] = 3; + pIndex = &buffer[ 7 ]; + mqttPacketInfo.remainingLength = 9; + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + uint16_t topicAliasMax = 1; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, topicAliasMax ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* PUBREL. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBREL; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( 1, packetIdentifier ); + /*Test Incoming Publish with Payload. */ + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; + buffer[ 6 ] = 0x00; + buffer[ 7 ] = 0x01; + mqttPacketInfo.remainingLength = 8; - /* PUBCOMP. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBCOMP; - status = MQTT_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &sessionPresent ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, NULL, 100, topicAliasMax ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( 1, packetIdentifier ); -} -/* ========================================================================== */ + /*Invalid Property Length. */ + pIndex = &buffer[ 6 ]; + mqttPacketInfo.remainingLength = 20; + propertyLength = encodeRemainingLength( pIndex, 20971556356235 ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, NULL, 100, topicAliasMax ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); -/** - * @brief Tests that MQTT_DeserializePublish works as intended. - */ -void test_MQTT_DeserializePublish( void ) + buffer[ 6 ] = 10; + buffer[ 7 ] = MQTT_SUBSCRIPTION_ID_ID; + pIndex = &buffer[ 8 ]; + encodeRemainingLength( pIndex, 20971556356235 ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, topicAliasMax ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} + +void test_incoming_publish_withPacketId( void ) { MQTTPacketInfo_t mqttPacketInfo; - MQTTPublishInfo_t publishInfo; + uint16_t packetIdentifier = 1; MQTTStatus_t status = MQTTSuccess; - uint8_t buffer[ 100 ]; - size_t bufferSize = sizeof( buffer ); - MQTTFixedBuffer_t fixedBuffer = { 0 }; - size_t packetSize = bufferSize; + uint8_t buffer[ 8 ] = { 0 }; + MQTTPropBuilder_t propBuffer = { 0 }; - size_t remainingLength = 0; - uint16_t packetIdentifier; + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH | 0x2; + mqttPacketInfo.pRemainingData = buffer; + mqttPacketInfo.remainingLength = 8; - fixedBuffer.pBuffer = buffer; - fixedBuffer.size = bufferSize; + buffer[ 0 ] = 0x00; + buffer[ 1 ] = 0x03; + buffer[ 2 ] = 0x61; + buffer[ 3 ] = 0x62; + buffer[ 4 ] = 0x63; + buffer[ 5 ] = 0x00; + buffer[ 6 ] = 0x01; + buffer[ 7 ] = 0x00; - const uint16_t PACKET_ID = 1; + MQTTPublishInfo_t publishIn; + ( void ) memset( &publishIn, 0x0, sizeof( publishIn ) ); - memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Verify parameters. */ - status = MQTT_DeserializePublish( NULL, &packetIdentifier, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_DeserializePublish( &mqttPacketInfo, NULL, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + buffer[ 6 ] = 0x00; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +void test_Invalid_IncomingPublish( void ) +{ + MQTTPacketInfo_t mqttPacketInfo; + uint16_t packetIdentifier = 1; + MQTTStatus_t status = MQTTSuccess; + uint8_t buffer[ 3 ] = { 0 }; + MQTTPropBuilder_t propBuffer = { 0 }; - /* Bad Packet Type. */ - mqttPacketInfo.type = 0x01; + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; mqttPacketInfo.pRemainingData = buffer; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + mqttPacketInfo.remainingLength = 3; - /* Incorrect flags. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH | 0xf; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + buffer[ 0 ] = 0x00; + buffer[ 1 ] = 0x01; + buffer[ 2 ] = 0x61; - /* QoS 0 bad remaining length. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; - mqttPacketInfo.remainingLength = 0; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* QoS 1 bad remaining length. */ - mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH | 0x2; - mqttPacketInfo.remainingLength = 0; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); + MQTTPublishInfo_t publishIn; + ( void ) memset( &publishIn, 0x0, sizeof( publishIn ) ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* QoS 1 invalid packet identifier. */ - mqttPacketInfo.remainingLength = 5; - buffer[ 0 ] = 0; - buffer[ 1 ] = 1; - buffer[ 2 ] = ( uint8_t ) 'a'; - buffer[ 3 ] = 0; - buffer[ 4 ] = 0; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH | 0x02; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishIn, &propBuffer, 100, 100 ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} - /* Create a PUBLISH packet to test. */ - memset( &publishInfo, 0x00, sizeof( publishInfo ) ); - setupPublishInfo( &publishInfo ); +/** + * @brief Tests that MQTT_SerializeDisconnect works as intended. + */ +void test_MQTT_SerializeDisconnect( void ) +{ + uint8_t buffer[ 25 + 2 * BUFFER_PADDING_LENGTH ]; + size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH; + size_t packetSize = bufferSize; + MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; + uint8_t expectedPacket[ 10 ] = { 0 }; + uint8_t * pIterator = expectedPacket; + MQTTStatus_t status = MQTTSuccess; + size_t remainingLength = 0; - /* Test serialization and deserialization of a QoS 0 PUBLISH. */ - publishInfo.qos = MQTTQoS0; + /* Buffer size less than disconnect request fails. */ + fixedBuffer.size = 1; + status = MQTT_SerializeDisconnect( NULL, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); + + /* NULL buffer fails. */ + status = MQTT_SerializeDisconnect( NULL, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, remainingLength, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* Restore the fixed buffer. */ + padAndResetBuffer( buffer, sizeof( buffer ) ); + fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; + fixedBuffer.size = bufferSize; + status = MQTT_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* Make sure buffer has enough space */ + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + /* Good case succeeds. */ + status = MQTT_SerializeDisconnect( NULL, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + *pIterator++ = MQTT_PACKET_TYPE_DISCONNECT; + pIterator += encodeRemainingLength( pIterator, remainingLength ); + *pIterator++ = MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION; + *pIterator = 0; /*Property length is 0 */ + TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); + + /*Test with properties. */ + + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); + + MQTTPropAdd_SessionExpiry( &propBuffer, 10 ); + status = MQTT_GetDisconnectPacketSize( &propBuffer, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* Make sure buffer has enough space */ + TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + padAndResetBuffer( buffer, sizeof( buffer ) ); + + status = MQTT_SerializeDisconnect( &propBuffer, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + pIterator = expectedPacket; + *pIterator++ = MQTT_PACKET_TYPE_DISCONNECT; + pIterator += encodeRemainingLength( pIterator, remainingLength ); + *pIterator++ = MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION; + *pIterator++ = 5; /*Property length is 2 */ + *pIterator++ = 0x11; /*Session Expiry ID*/ + pIterator += 3; + *pIterator = 10; /*Session Expiry value */ + TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); - /* Generate QoS 0 packet. */ - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); + /* test with null property buffer. */ + propBuffer.pBuffer = NULL; + status = MQTT_SerializeDisconnect( &propBuffer, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, remainingLength, &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); - status = MQTT_SerializePublish( &publishInfo, - 0, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* pFixedBuffer->pBuffer is NULL */ + fixedBuffer.pBuffer = NULL; + status = MQTT_SerializeDisconnect( &propBuffer, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION, remainingLength, &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +/** + * @brief Tests that MQTT_GetPingreqPacketSize works as intended. + */ +void test_MQTT_GetPingreqPacketSize( void ) +{ + MQTTStatus_t status; + size_t packetSize; - /* Deserialize QoS 0 packet. */ - mqttPacketInfo.type = buffer[ 0 ]; + /* Verify parameters. */ + status = MQTT_GetPingreqPacketSize( NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* We don't need to go through the trouble of calling MQTT_GetIncomingPacketTypeAndLength. - * We know the remaining length is < 128. */ - mqttPacketInfo.remainingLength = ( size_t ) buffer[ 1 ]; - mqttPacketInfo.pRemainingData = &buffer[ 2 ]; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); + /* Good case succeeds. A PINGREQ is 2 bytes. */ + status = MQTT_GetPingreqPacketSize( &packetSize ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( TEST_TOPIC_NAME_LENGTH, publishInfo.topicNameLength ); - TEST_ASSERT_EQUAL_MEMORY( TEST_TOPIC_NAME, publishInfo.pTopicName, TEST_TOPIC_NAME_LENGTH ); - TEST_ASSERT_EQUAL_INT( MQTT_SAMPLE_PAYLOAD_LEN, publishInfo.payloadLength ); - TEST_ASSERT_EQUAL_MEMORY( MQTT_SAMPLE_PAYLOAD, publishInfo.pPayload, MQTT_SAMPLE_PAYLOAD_LEN ); + TEST_ASSERT_EQUAL_INT( 2, packetSize ); +} - memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); - /* Reset publish info since its pointers now point to our serialized buffer. */ - setupPublishInfo( &publishInfo ); +/** + * @brief Tests that MQTT_SerializePingreq works as intended. + */ +void test_MQTT_SerializePingreq( void ) +{ + uint8_t buffer[ 10 + 2 * BUFFER_PADDING_LENGTH ]; + MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ] }; + uint8_t expectedPacket[ 2 ] = { MQTT_PACKET_TYPE_PINGREQ, 0 }; + MQTTStatus_t status = MQTTSuccess; - /* Test serialization and deserialization of a QoS 1 PUBLISH. */ - publishInfo.qos = MQTTQoS1; - /* Mark the publish as duplicate. */ - publishInfo.dup = true; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + /* Buffer size less than ping request fails. */ + fixedBuffer.size = 1; + status = MQTT_SerializePingreq( &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); - status = MQTT_SerializePublish( &publishInfo, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + /* NULL buffer fails. */ + status = MQTT_SerializePingreq( NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - mqttPacketInfo.type = buffer[ 0 ]; - mqttPacketInfo.remainingLength = ( size_t ) buffer[ 1 ]; - mqttPacketInfo.pRemainingData = &buffer[ 2 ]; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_TRUE( publishInfo.dup ); - TEST_ASSERT_EQUAL_INT( TEST_TOPIC_NAME_LENGTH, publishInfo.topicNameLength ); - TEST_ASSERT_EQUAL_MEMORY( TEST_TOPIC_NAME, publishInfo.pTopicName, TEST_TOPIC_NAME_LENGTH ); - TEST_ASSERT_EQUAL_INT( MQTT_SAMPLE_PAYLOAD_LEN, publishInfo.payloadLength ); - TEST_ASSERT_EQUAL_MEMORY( MQTT_SAMPLE_PAYLOAD, publishInfo.pPayload, MQTT_SAMPLE_PAYLOAD_LEN ); + /* Verify a NULL buffer in the fixed buffer struct fails */ + fixedBuffer.pBuffer = NULL; + status = MQTT_SerializePingreq( &fixedBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* QoS 2 PUBLISH. */ - setupPublishInfo( &publishInfo ); - publishInfo.qos = MQTTQoS2; - /* Remaining length and packet size should be same as before. */ - status = MQTT_SerializePublish( &publishInfo, - PACKET_ID, - remainingLength, - &fixedBuffer ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - mqttPacketInfo.type = buffer[ 0 ]; - mqttPacketInfo.remainingLength = ( size_t ) buffer[ 1 ]; - mqttPacketInfo.pRemainingData = &buffer[ 2 ]; - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); + /* Restore the fixed buffer. */ + fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; + + /* Good case succeeds. */ + fixedBuffer.size = 2; + padAndResetBuffer( buffer, sizeof( buffer ) ); + status = MQTT_SerializePingreq( &fixedBuffer ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( TEST_TOPIC_NAME_LENGTH, publishInfo.topicNameLength ); - TEST_ASSERT_EQUAL_MEMORY( TEST_TOPIC_NAME, publishInfo.pTopicName, TEST_TOPIC_NAME_LENGTH ); - TEST_ASSERT_EQUAL_INT( MQTT_SAMPLE_PAYLOAD_LEN, publishInfo.payloadLength ); - TEST_ASSERT_EQUAL_MEMORY( MQTT_SAMPLE_PAYLOAD, publishInfo.pPayload, MQTT_SAMPLE_PAYLOAD_LEN ); + checkBufferOverflow( buffer, sizeof( buffer ) ); + TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], 2 ); +} - /* Zero length payload. */ - memset( &publishInfo, 0x00, sizeof( publishInfo ) ); - publishInfo.pTopicName = TEST_TOPIC_NAME; - publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH; - publishInfo.payloadLength = 0; - publishInfo.qos = MQTTQoS0; +/** + * @brief Tests that MQTT_DeserializeAck works as intended with a PINGRESP. + */ +void test_MQTT_DeserializeAck_pingresp( void ) +{ + MQTTPacketInfo_t mqttPacketInfo; + MQTTStatus_t status = MQTTSuccess; - /* Generate packet. */ - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize ); + /* Bad remaining length. */ + ( void ) memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); + mqttPacketInfo.type = MQTT_PACKET_TYPE_PINGRESP; + mqttPacketInfo.remainingLength = MQTT_PACKET_PINGRESP_REMAINING_LENGTH + 1; + status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, NULL, NULL, 0, 0, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - status = MQTT_SerializePublish( &publishInfo, - 0, - remainingLength, - &fixedBuffer ); + /* Process a valid PINGRESP. */ + mqttPacketInfo.type = MQTT_PACKET_TYPE_PINGRESP; + mqttPacketInfo.remainingLength = MQTT_PACKET_PINGRESP_REMAINING_LENGTH; + mqttPacketInfo.pRemainingData = NULL; + status = MQTT_DeserializeAck( &mqttPacketInfo, NULL, NULL, NULL, 0, 0, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Deserialize packet. */ - mqttPacketInfo.type = buffer[ 0 ]; - mqttPacketInfo.remainingLength = ( size_t ) buffer[ 1 ]; - mqttPacketInfo.pRemainingData = &buffer[ 2 ]; - memset( &publishInfo, 0x00, sizeof( publishInfo ) ); - status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_INT( TEST_TOPIC_NAME_LENGTH, publishInfo.topicNameLength ); - TEST_ASSERT_EQUAL_MEMORY( TEST_TOPIC_NAME, publishInfo.pTopicName, TEST_TOPIC_NAME_LENGTH ); - TEST_ASSERT_EQUAL_INT( 0, publishInfo.payloadLength ); - TEST_ASSERT_NULL( publishInfo.pPayload ); + status = MQTT_DeserializeAck( NULL, NULL, NULL, NULL, 0, 0, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_GetIncomingPacketTypeAndLength works as intended. */ -void test_MQTT_GetIncomingPacketTypeAndLength( void ) +void test_MQTT_GetIncomingPacketTypeAndLength1( void ) { MQTTStatus_t status = MQTTSuccess; MQTTPacketInfo_t mqttPacket; @@ -1878,6 +3357,12 @@ void test_MQTT_GetIncomingPacketTypeAndLength( void ) status = MQTT_GetIncomingPacketTypeAndLength( mockReceive, &networkContext, &mqttPacket ); TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + /* Test with disconnect packet type. */ + bufPtr = buffer; + buffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT; + status = MQTT_GetIncomingPacketTypeAndLength( mockReceive, &networkContext, &mqttPacket ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + /* Test with invalid remaining length. */ bufPtr = buffer; buffer[ 0 ] = 0x20; /* CONN ACK */ @@ -1924,8 +3409,6 @@ void test_MQTT_GetIncomingPacketTypeAndLength( void ) TEST_ASSERT_EQUAL( MQTTBadResponse, status ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_SerializePublishHeaderWithoutTopic works as intended. */ @@ -1990,8 +3473,6 @@ void test_MQTT_SerializePublishHeaderWithoutTopic_QoS1( void ) TEST_ASSERT_EQUAL( buffer[ 3 ], 0U ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_SerializePublishHeaderWithoutTopic works as intended. */ @@ -2024,8 +3505,6 @@ void test_MQTT_SerializePublishHeaderWithoutTopic_QoS2( void ) TEST_ASSERT_EQUAL( buffer[ 3 ], 0U ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_SerializePublishHeaderWithoutTopic works as intended. */ @@ -2058,8 +3537,6 @@ void test_MQTT_SerializePublishHeaderWithoutTopic_retain( void ) TEST_ASSERT_EQUAL( buffer[ 3 ], 0U ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_SerializePublishHeaderWithoutTopic works as intended. */ @@ -2092,8 +3569,6 @@ void test_MQTT_SerializePublishHeaderWithoutTopic_Duplicate( void ) TEST_ASSERT_EQUAL( buffer[ 3 ], 0U ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_SerializePublishHeaderWithoutTopic works as intended. */ @@ -2129,8 +3604,6 @@ void test_MQTT_SerializePublishHeaderWithoutTopic_VariousFlagsSetTopicLength( vo TEST_ASSERT_EQUAL( buffer[ 3 ], 20U ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_SerializePublishHeader works as intended. */ @@ -2154,6 +3627,7 @@ void test_MQTT_SerializePublishHeader( void ) publishInfo.pTopicName = TEST_TOPIC_NAME; publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH; status = MQTT_SerializePublishHeader( NULL, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2161,6 +3635,7 @@ void test_MQTT_SerializePublishHeader( void ) TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, NULL, @@ -2168,6 +3643,7 @@ void test_MQTT_SerializePublishHeader( void ) TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2177,6 +3653,7 @@ void test_MQTT_SerializePublishHeader( void ) /* Verify a NULL buffer in the fixed buffer struct fails */ fixedBuffer.pBuffer = NULL; status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2189,6 +3666,7 @@ void test_MQTT_SerializePublishHeader( void ) publishInfo.pTopicName = NULL; publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH; status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2198,6 +3676,7 @@ void test_MQTT_SerializePublishHeader( void ) publishInfo.pTopicName = TEST_TOPIC_NAME; publishInfo.topicNameLength = 0; status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2208,6 +3687,7 @@ void test_MQTT_SerializePublishHeader( void ) /* 0 packet ID for QoS > 0. */ publishInfo.qos = MQTTQoS1; status = MQTT_SerializePublishHeader( &publishInfo, + NULL, 0, remainingLength, &fixedBuffer, @@ -2218,6 +3698,7 @@ void test_MQTT_SerializePublishHeader( void ) publishInfo.qos = MQTTQoS0; publishInfo.dup = true; status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2231,6 +3712,7 @@ void test_MQTT_SerializePublishHeader( void ) /* Buffer too small. */ fixedBuffer.size = 1; status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2239,10 +3721,11 @@ void test_MQTT_SerializePublishHeader( void ) fixedBuffer.size = bufferSize; /* Success case. */ - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); padAndResetBuffer( buffer, sizeof( buffer ) ); status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2263,16 +3746,18 @@ void test_MQTT_SerializePublishHeader( void ) pIterator += encodeString( pIterator, publishInfo.pTopicName, publishInfo.topicNameLength ); *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); + *pIterator++ = 0; TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); checkBufferOverflow( buffer, sizeof( buffer ) ); publishInfo.qos = MQTTQoS0; publishInfo.pPayload = "test"; publishInfo.payloadLength = 4; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); padAndResetBuffer( buffer, sizeof( buffer ) ); status = MQTT_SerializePublishHeader( &publishInfo, + NULL, 0, remainingLength, &fixedBuffer, @@ -2283,6 +3768,7 @@ void test_MQTT_SerializePublishHeader( void ) *pIterator++ = MQTT_PACKET_TYPE_PUBLISH; pIterator += encodeRemainingLength( pIterator, remainingLength ); pIterator += encodeString( pIterator, publishInfo.pTopicName, publishInfo.topicNameLength ); + *pIterator++ = 0; /* Payload should not be serialized. */ TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); checkBufferOverflow( buffer, sizeof( buffer ) ); @@ -2291,10 +3777,11 @@ void test_MQTT_SerializePublishHeader( void ) /* Again with QoS2 and dup. */ publishInfo.qos = MQTTQoS2; publishInfo.dup = true; - status = MQTT_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize ); + status = MQTT_GetPublishPacketSize( &publishInfo, NULL, &remainingLength, &packetSize, MQTT_MAX_PACKET_SIZE ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); padAndResetBuffer( buffer, sizeof( buffer ) ); status = MQTT_SerializePublishHeader( &publishInfo, + NULL, PACKET_ID, remainingLength, &fixedBuffer, @@ -2309,12 +3796,38 @@ void test_MQTT_SerializePublishHeader( void ) pIterator += encodeString( pIterator, publishInfo.pTopicName, publishInfo.topicNameLength ); *pIterator++ = UINT16_HIGH_BYTE( PACKET_ID ); *pIterator++ = UINT16_LOW_BYTE( PACKET_ID ); + *pIterator++ = 0; TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], packetSize ); checkBufferOverflow( buffer, sizeof( buffer ) ); + + /* test with publish properties */ + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); + MQTTPropAdd_PubMessageExpiry( &propBuffer, 100 ); + status = MQTT_SerializePublishHeader( &publishInfo, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer, + &headerSize ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*test with null buffer. */ + propBuffer.pBuffer = NULL; + status = MQTT_SerializePublishHeader( &publishInfo, + &propBuffer, + PACKET_ID, + remainingLength, + &fixedBuffer, + &headerSize ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } /* ========================================================================== */ + void test_MQTT_ProcessIncomingPacketTypeAndLength_PacketNULL( void ) { uint8_t pBuffer[ 100 ] = { 0 }; @@ -2326,8 +3839,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_PacketNULL( void ) TEST_ASSERT_EQUAL( MQTTBadParameter, status ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_BufferNULL( void ) { MQTTPacketInfo_t packetInfo; @@ -2339,8 +3850,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_BufferNULL( void ) TEST_ASSERT_EQUAL( MQTTBadParameter, status ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_IndexNULL( void ) { MQTTPacketInfo_t packetInfo; @@ -2352,8 +3861,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_IndexNULL( void ) TEST_ASSERT_EQUAL( MQTTBadParameter, status ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_NoData( void ) { MQTTPacketInfo_t packetInfo; @@ -2366,8 +3873,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_NoData( void ) TEST_ASSERT_EQUAL( MQTTNoDataAvailable, status ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_InvalidData( void ) { MQTTPacketInfo_t packetInfo; @@ -2385,8 +3890,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_InvalidData( void ) TEST_ASSERT_EQUAL( MQTTBadResponse, status ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_ValidDataOneByte( void ) { MQTTPacketInfo_t packetInfo; @@ -2404,8 +3907,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_ValidDataOneByte( void ) TEST_ASSERT_EQUAL( MQTTNeedMoreBytes, status ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_ValidDataTwoBytes( void ) { MQTTPacketInfo_t packetInfo; @@ -2427,8 +3928,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_ValidDataTwoBytes( void ) TEST_ASSERT_EQUAL( packetInfo.headerLength, 2U ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_InvalidLength( void ) { MQTTPacketInfo_t packetInfo; @@ -2453,8 +3952,6 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_InvalidLength( void ) TEST_ASSERT_EQUAL( MQTTBadResponse, status ); } -/* ========================================================================== */ - void test_MQTT_ProcessIncomingPacketTypeAndLength_NonConformingLength( void ) { MQTTPacketInfo_t packetInfo; @@ -2478,404 +3975,1043 @@ void test_MQTT_ProcessIncomingPacketTypeAndLength_NonConformingLength( void ) TEST_ASSERT_EQUAL( MQTTBadResponse, status ); } -/* ========================================================================== */ - /** - * @brief Tests that MQTT_SerializeAck works as intended. + * @brief Tests that MQTT_DeserializePublish works as intended. */ +void test_MQTT_DeserializePublish( void ) +{ + MQTTPacketInfo_t mqttPacketInfo; + MQTTPublishInfo_t publishInfo; + MQTTStatus_t status = MQTTSuccess; + uint8_t buffer[ 100 ]; + MQTTPropBuilder_t propBuffer = { 0 }; + + uint16_t packetIdentifier; + + + memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) ); + + /* Verify parameters. */ + status = MQTT_DeserializePublish( NULL, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_DeserializePublish( &mqttPacketInfo, NULL, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, NULL, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* Bad Packet Type. */ + mqttPacketInfo.type = 0x01; + mqttPacketInfo.pRemainingData = buffer; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* Incorrect flags. */ + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH | 0xf; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /* QoS 0 bad remaining length. */ + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH; + mqttPacketInfo.remainingLength = 0; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /* QoS 1 bad remaining length. */ + mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBLISH | 0x2; + mqttPacketInfo.remainingLength = 0; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /* QoS 1 invalid packet identifier. */ + mqttPacketInfo.remainingLength = 5; + buffer[ 0 ] = 0; + buffer[ 1 ] = 1; + buffer[ 2 ] = ( uint8_t ) 'a'; + buffer[ 3 ] = 0; + buffer[ 4 ] = 0; + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 100, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + /*Invalid max packet size*/ + status = MQTT_DeserializePublish( &mqttPacketInfo, &packetIdentifier, &publishInfo, &propBuffer, 1, 100 ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} +void test_serializeHeaders( void ) +{ + uint8_t buffer[ 4 ]; + + MQTT_SerializeSubscribeHeader( 3, buffer, 1 ); + TEST_ASSERT_EQUAL( buffer[ 0 ], MQTT_PACKET_TYPE_SUBSCRIBE ); + MQTT_SerializeUnsubscribeHeader( 3, buffer, 1 ); + TEST_ASSERT_EQUAL( buffer[ 0 ], MQTT_PACKET_TYPE_UNSUBSCRIBE ); + uint8_t buf[ 22 ]; + MQTTConnectInfo_t pConnectInfo; + pConnectInfo.cleanSession = true; + pConnectInfo.pUserName = "abc"; + pConnectInfo.pPassword = "def"; + pConnectInfo.keepAliveSeconds = 100; + MQTTPublishInfo_t pWillInfo; + pWillInfo.qos = MQTTQoS1; + pWillInfo.retain = true; + MQTT_SerializeConnectFixedHeader( buf, &pConnectInfo, &pWillInfo, 20 ); + pWillInfo.qos = MQTTQoS2; + MQTT_SerializeConnectFixedHeader( buf, &pConnectInfo, &pWillInfo, 20 ); +} + +void test_OptionalProperties( void ) +{ + MQTTStatus_t mqttStatus = MQTTSuccess; + MQTTPropBuilder_t propBuilder; + MQTTPropBuilder_t prop1; + + prop1.pBuffer = NULL; + uint8_t buf[ 100 ]; + size_t bufLength = sizeof( buf ); + propBuilder.pBuffer = buf; + propBuilder.bufferLength = bufLength; + propBuilder.currentIndex = 0; + propBuilder.fieldSet = 0; + + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_SubscribeId( &propBuilder, 2 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_SubscribeId( &propBuilder, 2 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_SubscribeId( &propBuilder, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_SubscribeId( NULL, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_SubscribeId( &prop1, 2 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + MQTTUserProperty_t userProperty; + memset( &userProperty, 0x0, sizeof( userProperty ) ); + userProperty.pKey = "abc"; + userProperty.pValue = "def"; + userProperty.keyLength = 3; + userProperty.valueLength = 3; + mqttStatus = MQTTPropAdd_UserProp( &( propBuilder ), &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + mqttStatus = MQTTPropAdd_UserProp( NULL, &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_UserProp( &( propBuilder ), NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_UserProp( &prop1, &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + userProperty.pKey = NULL; + mqttStatus = MQTTPropAdd_UserProp( &propBuilder, &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + userProperty.pKey = "abc"; + userProperty.pValue = NULL; + mqttStatus = MQTTPropAdd_UserProp( &propBuilder, &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + userProperty.pValue = "def"; + userProperty.keyLength = 0; + mqttStatus = MQTTPropAdd_UserProp( &propBuilder, &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + userProperty.keyLength = 3; + userProperty.valueLength = 0; + mqttStatus = MQTTPropAdd_UserProp( &propBuilder, &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_SessionExpiry( NULL, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_SessionExpiry( &prop1, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_SessionExpiry( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_SessionExpiry( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnReceiveMax( NULL, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnReceiveMax( &prop1, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnReceiveMax( &( propBuilder ), 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnReceiveMax( &( propBuilder ), 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnReceiveMax( &( propBuilder ), 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnMaxPacketSize( NULL, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnMaxPacketSize( &prop1, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnMaxPacketSize( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnMaxPacketSize( &propBuilder, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnMaxPacketSize( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnTopicAliasMax( NULL, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnTopicAliasMax( &prop1, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnTopicAliasMax( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnTopicAliasMax( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnRequestRespInfo( NULL, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnRequestRespInfo( &prop1, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnRequestRespInfo( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + propBuilder.fieldSet = 0; + mqttStatus = MQTTPropAdd_ConnRequestRespInfo( &propBuilder, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnRequestRespInfo( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnRequestProbInfo( NULL, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnRequestProbInfo( &prop1, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnRequestProbInfo( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + propBuilder.fieldSet = 0; + mqttStatus = MQTTPropAdd_ConnRequestProbInfo( &propBuilder, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnRequestProbInfo( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /*Test Auth Data before Auth Method.*/ + mqttStatus = MQTTPropAdd_ConnAuthData( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnAuthMethod( NULL, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthMethod( &propBuilder, NULL, 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthMethod( &propBuilder, "abc", 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthMethod( &prop1, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthMethod( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthMethod( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnAuthData( NULL, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthData( &propBuilder, NULL, 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthData( &propBuilder, "abc", 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthData( &prop1, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthData( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_ConnAuthData( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubPayloadFormat( NULL, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubPayloadFormat( &prop1, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubPayloadFormat( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + propBuilder.fieldSet = 0; + mqttStatus = MQTTPropAdd_PubPayloadFormat( &propBuilder, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_PubPayloadFormat( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubMessageExpiry( NULL, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubMessageExpiry( &prop1, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubMessageExpiry( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_PubMessageExpiry( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubTopicAlias( NULL, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubTopicAlias( &prop1, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubTopicAlias( &propBuilder, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubTopicAlias( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_PubTopicAlias( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubResponseTopic( NULL, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubResponseTopic( &prop1, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder, NULL, 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder, "abc", 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder, "abc/#", 5 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder, "abc/+/def", 9 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + MQTTPropBuilder_t propBuilder2; + uint8_t buf2[ 500 ]; + propBuilder2.pBuffer = buf2; + propBuilder2.bufferLength = sizeof( buf2 ); + propBuilder2.currentIndex = 0; + propBuilder2.fieldSet = 0; + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder2, "abc/#/def", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubCorrelationData( NULL, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubCorrelationData( &( prop1 ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubCorrelationData( &( propBuilder ), NULL, 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubCorrelationData( &( propBuilder ), "abc", 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubCorrelationData( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubCorrelationData( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + + mqttStatus = MQTTPropAdd_PubContentType( NULL, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubContentType( &( prop1 ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubContentType( &( propBuilder ), NULL, 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubContentType( &( propBuilder ), "abc", 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_PubContentType( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_PubContentType( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_ReasonString( NULL, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ReasonString( &( prop1 ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ReasonString( &( propBuilder ), NULL, 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ReasonString( &( propBuilder ), "abc", 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_ReasonString( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_ReasonString( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTTPropAdd_WillDelayInterval( NULL, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_WillDelayInterval( &( prop1 ), 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + mqttStatus = MQTTPropAdd_WillDelayInterval( &( propBuilder ), 10 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + mqttStatus = MQTTPropAdd_WillDelayInterval( &( propBuilder ), 10 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); +} + +void test_MQTTPropAdd_NoMemory( void ) +{ + MQTTStatus_t mqttStatus = MQTTSuccess; + MQTTPropBuilder_t propBuilder; + uint8_t buf[ 1 ]; + size_t bufLength = sizeof( buf ); + + propBuilder.pBuffer = buf; + propBuilder.bufferLength = bufLength; + propBuilder.currentIndex = 0; + propBuilder.fieldSet = 0; + + mqttStatus = MQTTPropAdd_ReasonString( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubContentType( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubCorrelationData( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubCorrelationData( &( propBuilder ), "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubResponseTopic( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubTopicAlias( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubMessageExpiry( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_PubPayloadFormat( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnAuthMethod( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnAuthData( &propBuilder, "abc", 3 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnRequestProbInfo( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnRequestRespInfo( &propBuilder, 1 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnTopicAliasMax( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnMaxPacketSize( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_ConnReceiveMax( &( propBuilder ), 10 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_SessionExpiry( &propBuilder, 10 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_WillDelayInterval( &( propBuilder ), 10 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + MQTTUserProperty_t userProperty; + memset( &userProperty, 0x0, sizeof( userProperty ) ); + userProperty.pKey = "abc"; + userProperty.pValue = "def"; + userProperty.keyLength = 3; + userProperty.valueLength = 3; + mqttStatus = MQTTPropAdd_UserProp( &( propBuilder ), &userProperty ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); + + mqttStatus = MQTTPropAdd_SubscribeId( &propBuilder, 2 ); + TEST_ASSERT_EQUAL_INT( MQTTNoMemory, mqttStatus ); +} +void test_updateContextWithConnectProps( void ) +{ + MQTTStatus_t mqttStatus = MQTTSuccess; + MQTTPropBuilder_t propBuilder; + uint8_t buffer[ 50 ]; + MQTTConnectProperties_t connectProps; + size_t bufLength = sizeof( buffer ); + + propBuilder.pBuffer = buffer; + propBuilder.bufferLength = bufLength; + propBuilder.currentIndex = 0; + propBuilder.fieldSet = 0; + + mqttStatus = updateContextWithConnectProps( &propBuilder, &connectProps ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + uint8_t * pIndex = buffer; + pIndex = serializeuint_16( pIndex, MQTT_RECEIVE_MAX_ID ); + pIndex = serializeuint_32( pIndex, MQTT_SESSION_EXPIRY_ID ); + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_MAX_ID ); + pIndex = serializeuint_32( pIndex, MQTT_MAX_PACKET_SIZE_ID ); + pIndex = serializeutf_8( pIndex, MQTT_AUTH_METHOD_ID ); + pIndex = serializeuint_8( pIndex, MQTT_REQUEST_PROBLEM_ID ); + pIndex = serializeuint_8( pIndex, MQTT_REQUEST_RESPONSE_ID ); + pIndex = serializeutf_8pair( pIndex ); + + propBuilder.currentIndex = 27; + propBuilder.currentIndex += 13; + mqttStatus = updateContextWithConnectProps( &propBuilder, &connectProps ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + mqttStatus = updateContextWithConnectProps( NULL, &connectProps ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + mqttStatus = updateContextWithConnectProps( &propBuilder, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + propBuilder.pBuffer = NULL; + mqttStatus = updateContextWithConnectProps( &propBuilder, &connectProps ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + propBuilder.pBuffer = buffer; + propBuilder.currentIndex = 5; + mqttStatus = updateContextWithConnectProps( &propBuilder, &connectProps ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, mqttStatus ); + + propBuilder.pBuffer = pIndex; + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + propBuilder.currentIndex = 10; + mqttStatus = updateContextWithConnectProps( &propBuilder, &connectProps ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); +} + void test_MQTT_SerializeAck( void ) { - uint8_t buffer[ 10 + 2 * BUFFER_PADDING_LENGTH ]; - uint8_t expectedPacket[ MQTT_PUBLISH_ACK_PACKET_SIZE ]; - size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH; + MQTTFixedBuffer_t fixedBuffer; + uint8_t buf[ 50 ]; + + fixedBuffer.pBuffer = buf; + fixedBuffer.size = sizeof( buf ); + MQTTStatus_t status = MQTTSuccess; - MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize }; - uint8_t packetType = MQTT_PACKET_TYPE_PUBACK; + status = MQTT_SerializeAck( &fixedBuffer, MQTT_PACKET_TYPE_PUBACK, 100 ); + TEST_ASSERT_EQUAL_INT( status, MQTTSuccess ); - const uint16_t PACKET_ID = 1; + status = MQTT_SerializeAck( &fixedBuffer, MQTT_PACKET_TYPE_PUBACK, 0 ); + TEST_ASSERT_EQUAL_INT( status, MQTTBadParameter ); - expectedPacket[ 0 ] = packetType; - expectedPacket[ 1 ] = 2U; - expectedPacket[ 2 ] = UINT16_HIGH_BYTE( PACKET_ID ); - expectedPacket[ 3 ] = UINT16_LOW_BYTE( PACKET_ID ); - /* Verify invalid parameter failures. */ - status = MQTT_SerializeAck( NULL, packetType, PACKET_ID ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_SerializeAck( &fixedBuffer, MQTT_PACKET_TYPE_SUBACK, 100 ); + TEST_ASSERT_EQUAL_INT( status, MQTTBadParameter ); - status = MQTT_SerializeAck( &fixedBuffer, packetType, 0 ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_SerializeAck( NULL, MQTT_PACKET_TYPE_PUBACK, 100 ); + TEST_ASSERT_EQUAL_INT( status, MQTTBadParameter ); - /* Verify a NULL buffer in the fixed buffer struct fails */ fixedBuffer.pBuffer = NULL; - status = MQTT_SerializeAck( &fixedBuffer, packetType, PACKET_ID ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + status = MQTT_SerializeAck( &fixedBuffer, MQTT_PACKET_TYPE_PUBACK, 100 ); + TEST_ASSERT_EQUAL_INT( status, MQTTBadParameter ); - /* Restore the fixed buffer. */ - fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ]; + fixedBuffer.pBuffer = buf; + fixedBuffer.size = 1; + status = MQTT_SerializeAck( &fixedBuffer, MQTT_PACKET_TYPE_PUBACK, 100 ); + TEST_ASSERT_EQUAL_INT( status, MQTTNoMemory ); +} - /* Not a PUBACK, PUBREC, PUBREL, or PUBCOMP. */ - status = MQTT_SerializeAck( &fixedBuffer, MQTT_PACKET_TYPE_CONNACK, PACKET_ID ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +void test_MQTTV5_SerializeDisconnect( void ) +{ + uint8_t buf[ 10 ]; - /* An ack is 4 bytes. */ - fixedBuffer.size = 3; - status = MQTT_SerializeAck( &fixedBuffer, packetType, PACKET_ID ); - TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); - fixedBuffer.size = bufferSize; + MQTT_SerializeDisconnectFixed( buf, 0x00, 10 ); + TEST_ASSERT_EQUAL( buf[ 0 ], MQTT_PACKET_TYPE_DISCONNECT ); - /* Good case succeeds. */ - padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeAck( &fixedBuffer, packetType, PACKET_ID ); - TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], MQTT_PUBLISH_ACK_PACKET_SIZE ); - checkBufferOverflow( buffer, sizeof( buffer ) ); + MQTT_SerializeAckFixed( buf, MQTT_PACKET_TYPE_SUBACK, 10, 10, 0x00 ); + TEST_ASSERT_EQUAL( buf[ 0 ], MQTT_PACKET_TYPE_SUBACK ); +} - /* QoS 2 acks. */ - packetType = MQTT_PACKET_TYPE_PUBREC; - expectedPacket[ 0 ] = packetType; - padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeAck( &fixedBuffer, packetType, PACKET_ID ); +void test_validatePublishProperties( void ) +{ + MQTTStatus_t status = MQTTSuccess; + uint16_t serverTopicAliasMax; + uint16_t topicAlias; + MQTTPropBuilder_t propBuilder; + uint8_t buffer[ 50 ]; + size_t bufLength = sizeof( buffer ); + + propBuilder.pBuffer = buffer; + propBuilder.bufferLength = bufLength; + propBuilder.currentIndex = 0; + propBuilder.fieldSet = 0; + + status = MQTT_ValidatePublishProperties( 1, &propBuilder, &topicAlias ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], MQTT_PUBLISH_ACK_PACKET_SIZE ); - checkBufferOverflow( buffer, sizeof( buffer ) ); - packetType = MQTT_PACKET_TYPE_PUBREL; - expectedPacket[ 0 ] = packetType; - padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeAck( &fixedBuffer, packetType, PACKET_ID ); + uint8_t * pIndex = buffer; + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + propBuilder.currentIndex = 3; + + /*Invalid Topic Alias*/ + serverTopicAliasMax = 2; + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + serverTopicAliasMax = 300; + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], MQTT_PUBLISH_ACK_PACKET_SIZE ); - checkBufferOverflow( buffer, sizeof( buffer ) ); - packetType = MQTT_PACKET_TYPE_PUBCOMP; - expectedPacket[ 0 ] = packetType; - padAndResetBuffer( buffer, sizeof( buffer ) ); - status = MQTT_SerializeAck( &fixedBuffer, packetType, PACKET_ID ); + /*Property length field less than the actual length of the property.*/ + propBuilder.currentIndex = 2; + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + propBuilder.currentIndex = 3; + + pIndex = buffer; + pIndex = serializeutf_8( pIndex, MQTT_RESPONSE_TOPIC_ID ); + propBuilder.currentIndex = 7; + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - TEST_ASSERT_EQUAL_MEMORY( expectedPacket, &buffer[ BUFFER_PADDING_LENGTH ], MQTT_PUBLISH_ACK_PACKET_SIZE ); - checkBufferOverflow( buffer, sizeof( buffer ) ); } -/* ===================== Testing MQTT_SerializeConnect ===================== */ - -/** - * @brief Check the serialization of an MQTT CONNECT packet in the given buffer, - * following the same order in serializeConnectPacket. - * - * @param[in] pConnectInfo MQTT CONNECT packet parameters. - * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used. - * @param[in] remainingLength Remaining Length of MQTT CONNECT packet. - * @param[in] pBuffer Buffer to check packet serialization. - * - */ -static void verifySerializedConnectPacket( const MQTTConnectInfo_t * const pConnectInfo, - const MQTTPublishInfo_t * const pWillInfo, - size_t remainingLength, - const MQTTFixedBuffer_t * const pBuffer ) -{ - uint8_t connectFlags = 0U; - uint8_t encodedRemainingLength = 0U; - uint8_t encodedStringLength = 0U; - uint8_t * pIndex = NULL; +void test_validateSubscribeProperties( void ) +{ + uint8_t isSubIdAvailable = 0; + MQTTStatus_t status = MQTTSuccess; + MQTTPropBuilder_t propBuilder; + uint8_t buffer[ 50 ]; + size_t bufLength = sizeof( buffer ); - pIndex = pBuffer->pBuffer; - /* The first byte in the CONNECT packet is the control packet type. */ - TEST_ASSERT_EQUAL_MESSAGE( MQTT_PACKET_TYPE_CONNECT, *pIndex, "MQTT_PACKET_TYPE_CONNECT is not equal to *pIndex" ); - pIndex++; + propBuilder.pBuffer = NULL; + propBuilder.bufferLength = bufLength; + propBuilder.currentIndex = 0; + propBuilder.fieldSet = 0; - /* The remaining length of the CONNECT packet is encoded starting from the - * second byte. The remaining length does not include the length of the fixed - * header or the encoding of the remaining length. */ - encodedRemainingLength = encodeRemainingLength( remainingLengthBuffer, remainingLength ); - TEST_ASSERT_EQUAL_MEMORY( remainingLengthBuffer, pIndex, encodedRemainingLength ); - pIndex += encodedRemainingLength; - - /* The string "MQTT" is placed at the beginning of the CONNECT packet's variable - * header. This string is 4 bytes long. */ - encodedStringLength = encodeString( encodedStringBuffer, "MQTT", 4 ); - TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength ); - pIndex += encodedStringLength; - - /* The MQTT protocol version is the second field of the variable header. */ - TEST_ASSERT_EQUAL( MQTT_VERSION_3_1_1, *pIndex ); - pIndex++; + status = MQTT_ValidateSubscribeProperties( isSubIdAvailable, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Set the clean session flag if needed. */ - if( pConnectInfo->cleanSession == true ) - { - UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_CLEAN ); - } + status = MQTT_ValidateSubscribeProperties( isSubIdAvailable, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Set the flags for username and password if provided. */ - if( pConnectInfo->pUserName != NULL ) - { - UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_USERNAME ); - } + propBuilder.pBuffer = buffer; + status = MQTT_ValidateSubscribeProperties( isSubIdAvailable, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - if( pConnectInfo->pPassword != NULL ) - { - UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_PASSWORD ); - } + uint8_t * pIndex = buffer; + *pIndex = MQTT_SUBSCRIPTION_ID_ID; + pIndex++; + *pIndex = 2; + propBuilder.currentIndex = 2; + status = MQTT_ValidateSubscribeProperties( isSubIdAvailable, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Set will flag if a Last Will and Testament is provided. */ - if( pWillInfo != NULL ) - { - UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL ); + isSubIdAvailable = 1; + status = MQTT_ValidateSubscribeProperties( isSubIdAvailable, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - /* Flags only need to be changed for Will QoS 1 or 2. */ - if( pWillInfo->qos == MQTTQoS1 ) - { - UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS1 ); - } - else if( pWillInfo->qos == MQTTQoS2 ) - { - UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS2 ); - } - else - { - /* Empty else MISRA 15.7 */ - } + pIndex = buffer; + pIndex = serializeutf_8pair( pIndex ); + propBuilder.currentIndex += 11; + status = MQTT_ValidateSubscribeProperties( 1, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); - if( pWillInfo->retain == true ) - { - UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_RETAIN ); - } - } + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + propBuilder.currentIndex += 2; + status = MQTT_ValidateSubscribeProperties( 1, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - TEST_ASSERT_EQUAL( connectFlags, *pIndex ); - pIndex++; + /*Invalid Subscription Id, exceeding 4 bytes. */ + pIndex = buffer; + *pIndex++ = MQTT_SUBSCRIPTION_ID_ID; + encodeRemainingLength( pIndex, 20971556356235 ); + propBuilder.currentIndex = 10; + status = MQTT_ValidateSubscribeProperties( 1, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); +} - /* Verify the 2 bytes of the keep alive interval into the CONNECT packet. */ - TEST_ASSERT_EQUAL( UINT16_HIGH_BYTE( pConnectInfo->keepAliveSeconds ), - *pIndex ); +void test_getProps( void ) +{ + MQTTStatus_t status = MQTTSuccess; + MQTTPropBuilder_t propBuffer; + MQTTPropBuilder_t propBuffer1; + + propBuffer1.pBuffer = NULL; + propBuffer1.bufferLength = 0; + propBuffer1.currentIndex = 0; + propBuffer1.fieldSet = 0; + size_t currIndex; + + + uint8_t buffer[ 500 ] = { 0 }; + size_t bufLength = 500; + + propBuffer.pBuffer = buffer; + propBuffer.bufferLength = bufLength; + propBuffer.currentIndex = 0; + propBuffer.fieldSet = 0; + + uint8_t * pIndex = buffer; + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + pIndex = serializeutf_8( pIndex, MQTT_RESPONSE_TOPIC_ID ); + pIndex = serializeutf_8( pIndex, MQTT_CORRELATION_DATA_ID ); + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + pIndex = serializeutf_8( pIndex, MQTT_CONTENT_TYPE_ID ); + *pIndex = MQTT_SUBSCRIPTION_ID_ID; pIndex++; - TEST_ASSERT_EQUAL( UINT16_LOW_BYTE( pConnectInfo->keepAliveSeconds ), - *pIndex ); + *pIndex = 2; pIndex++; + pIndex = serializeuint_32( pIndex, MQTT_SESSION_EXPIRY_ID ); + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_MAX_ID ); + pIndex = serializeuint_16( pIndex, MQTT_RECEIVE_MAX_ID ); + pIndex = serializeuint_8( pIndex, MQTT_MAX_QOS_ID ); + pIndex = serializeuint_8( pIndex, MQTT_RETAIN_AVAILABLE_ID ); + pIndex = serializeuint_32( pIndex, MQTT_MAX_PACKET_SIZE_ID ); + pIndex = serializeutf_8( pIndex, MQTT_ASSIGNED_CLIENT_ID ); + pIndex = serializeuint_8( pIndex, MQTT_WILDCARD_ID ); + pIndex = serializeuint_8( pIndex, MQTT_SUB_AVAILABLE_ID ); + pIndex = serializeutf_8pair( pIndex ); + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + pIndex = serializeutf_8( pIndex, MQTT_SERVER_REF_ID ); + pIndex = serializeuint_8( pIndex, MQTT_SHARED_SUB_ID ); + pIndex = serializeuint_16( pIndex, MQTT_SERVER_KEEP_ALIVE_ID ); + pIndex = serializeutf_8( pIndex, MQTT_RESPONSE_INFO_ID ); + pIndex = serializeutf_8( pIndex, MQTT_AUTH_METHOD_ID ); + pIndex = serializeutf_8( pIndex, MQTT_AUTH_DATA_ID ); + + uint16_t topicAlias; + uint8_t payloadFormat; + const char * pResponseTopic; + uint16_t responseTopicLength; + const void * correlationData; + uint16_t correlationLength; + uint32_t messageExpiry; + const char * pContentType; + uint16_t contentTypeLength; + size_t subscriptionId; + uint32_t sessionExpiry; + uint16_t aliasMax; + uint16_t receiveMax; + uint8_t maxQoS; + uint8_t retainAvailable; + uint32_t maxPacketSize; + const char * pClientId; + uint16_t clientIdLength; + uint8_t wildcard; + uint8_t subAvailable; + uint8_t propertyId; + const char * pUserPropKey; + uint16_t pUserPropKeyLen; + const char * pUserPropVal; + uint16_t pUserPropValLen; + const char * pReasonString; + uint16_t reasonStringLength; + uint8_t sharedSubAvailable; + uint16_t serverKeepAlive; + const char * pResponseInfo; + uint16_t responseInfoLength; + const char * pAuthMethod; + uint16_t authMethodLength; + const char * pAuthData; + uint16_t authDataLength; + + + size_t counter = 0U; + status = MQTT_IncomingGetNextProp( NULL, &propertyId ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Verify the client identifier into the CONNECT packet. */ - encodedStringLength = encodeString( encodedStringBuffer, - pConnectInfo->pClientIdentifier, - pConnectInfo->clientIdentifierLength ); - TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength ); - pIndex += encodedStringLength; + status = MQTT_IncomingGetNextProp( &propBuffer1, &propertyId ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - /* Verify the will topic name and message into the CONNECT packet if provided. */ - if( pWillInfo != NULL ) - { - encodedStringLength = encodeString( encodedStringBuffer, - pWillInfo->pTopicName, - pWillInfo->topicNameLength ); - TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength ); - pIndex += encodedStringLength; - encodedStringLength = encodeString( encodedStringBuffer, - pWillInfo->pPayload, - ( uint16_t ) pWillInfo->payloadLength ); - TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength ); - pIndex += encodedStringLength; - } + status = MQTT_IncomingGetNextProp( &propBuffer, &propertyId ); + counter++; - /* Verify the user name if provided. */ - if( pConnectInfo->pUserName != NULL ) + while( status == MQTTSuccess ) { - encodedStringLength = encodeString( encodedStringBuffer, - pConnectInfo->pUserName, - pConnectInfo->userNameLength ); - TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength ); - pIndex += encodedStringLength; - } + switch( propertyId ) + { + case MQTT_TOPIC_ALIAS_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubTopicAlias( NULL, &topicAlias ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubTopicAlias( &propBuffer1, &topicAlias ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubTopicAlias( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_PubTopicAlias( &propBuffer, &topicAlias ) ); + break; + + case MQTT_PAYLOAD_FORMAT_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubPayloadFormatIndicator( NULL, &payloadFormat ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubPayloadFormatIndicator( &propBuffer1, &payloadFormat ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubPayloadFormatIndicator( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_PubPayloadFormatIndicator( &propBuffer, &payloadFormat ) ); + break; + + case MQTT_RESPONSE_TOPIC_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubResponseTopic( NULL, &pResponseTopic, &responseTopicLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubResponseTopic( &propBuffer1, &pResponseTopic, &responseTopicLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubResponseTopic( &propBuffer, NULL, &responseTopicLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubResponseTopic( &propBuffer, &pResponseTopic, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_PubResponseTopic( &propBuffer, &pResponseTopic, &responseTopicLength ) ); + propBuffer.bufferLength = 7; + currIndex = propBuffer.currentIndex; + propBuffer.currentIndex = 6; + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, MQTTPropGet_PubResponseTopic( &propBuffer, &pResponseTopic, &responseTopicLength ) ); + propBuffer.currentIndex = 4; + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, MQTTPropGet_PubResponseTopic( &propBuffer, &pResponseTopic, &responseTopicLength ) ); + propBuffer.bufferLength = 500; + propBuffer.currentIndex = currIndex; + break; + + case MQTT_CORRELATION_DATA_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubCorrelationData( NULL, &correlationData, &correlationLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubCorrelationData( &propBuffer1, &correlationData, &correlationLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubCorrelationData( &propBuffer, NULL, &correlationLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubCorrelationData( &propBuffer, &correlationData, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_PubCorrelationData( &propBuffer, &correlationData, &correlationLength ) ); + propBuffer.bufferLength = 7; + currIndex = propBuffer.currentIndex; + propBuffer.currentIndex = 6; + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, MQTTPropGet_PubCorrelationData( &propBuffer, &correlationData, &correlationLength ) ); + propBuffer.currentIndex = 4; + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, MQTTPropGet_PubCorrelationData( &propBuffer, &correlationData, &correlationLength ) ); + propBuffer.bufferLength = 500; + propBuffer.currentIndex = currIndex; + break; + + case MQTT_MSG_EXPIRY_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubMessageExpiryInterval( NULL, &messageExpiry ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubMessageExpiryInterval( &propBuffer1, &messageExpiry ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubMessageExpiryInterval( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_PubMessageExpiryInterval( &propBuffer, &messageExpiry ) ); + break; + + case MQTT_CONTENT_TYPE_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubContentType( NULL, &pContentType, &contentTypeLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubContentType( &propBuffer1, &pContentType, &contentTypeLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubContentType( &propBuffer, NULL, &contentTypeLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubContentType( &propBuffer, &pContentType, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_PubContentType( &propBuffer, &pContentType, &contentTypeLength ) ); + break; + + case MQTT_SUBSCRIPTION_ID_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubSubscriptionId( NULL, &subscriptionId ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubSubscriptionId( &propBuffer1, &subscriptionId ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_PubSubscriptionId( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_PubSubscriptionId( &propBuffer, &subscriptionId ) ); + break; + + case MQTT_SESSION_EXPIRY_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_SessionExpiry( NULL, &sessionExpiry ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_SessionExpiry( &propBuffer1, &sessionExpiry ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_SessionExpiry( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_SessionExpiry( &propBuffer, &sessionExpiry ) ); + break; + + case MQTT_TOPIC_ALIAS_MAX_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnTopicAliasMax( NULL, &aliasMax ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnTopicAliasMax( &propBuffer1, &aliasMax ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnTopicAliasMax( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnTopicAliasMax( &propBuffer, &aliasMax ) ); + break; + + case MQTT_RECEIVE_MAX_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnReceiveMax( NULL, &receiveMax ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnReceiveMax( &propBuffer1, &receiveMax ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnReceiveMax( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnReceiveMax( &propBuffer, &receiveMax ) ); + break; + + case MQTT_MAX_QOS_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnMaxQos( NULL, &maxQoS ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnMaxQos( &propBuffer1, &maxQoS ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnMaxQos( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnMaxQos( &propBuffer, &maxQoS ) ); + break; + + case MQTT_RETAIN_AVAILABLE_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnRetainAvailable( NULL, &retainAvailable ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnRetainAvailable( &propBuffer1, &retainAvailable ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnRetainAvailable( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnRetainAvailable( &propBuffer, &retainAvailable ) ); + break; + + case MQTT_MAX_PACKET_SIZE_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnMaxPacketSize( NULL, &maxPacketSize ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnMaxPacketSize( &propBuffer1, &maxPacketSize ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnMaxPacketSize( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnMaxPacketSize( &propBuffer, &maxPacketSize ) ); + break; + + case MQTT_ASSIGNED_CLIENT_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnClientId( NULL, &pClientId, &clientIdLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnClientId( &propBuffer1, &pClientId, &clientIdLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnClientId( &propBuffer, NULL, &clientIdLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnClientId( &propBuffer, &pClientId, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnClientId( &propBuffer, &pClientId, &clientIdLength ) ); + break; + + case MQTT_WILDCARD_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnWildcard( NULL, &wildcard ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnWildcard( &propBuffer1, &wildcard ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnWildcard( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnWildcard( &propBuffer, &wildcard ) ); + break; + + case MQTT_SUB_AVAILABLE_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnSubId( NULL, &subAvailable ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnSubId( &propBuffer1, &subAvailable ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnSubId( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnSubId( &propBuffer, &subAvailable ) ); + break; + + case MQTT_USER_PROPERTY_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_UserProp( NULL, &pUserPropKey, &pUserPropKeyLen, &pUserPropVal, &pUserPropValLen ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_UserProp( &propBuffer1, &pUserPropKey, &pUserPropKeyLen, &pUserPropVal, &pUserPropValLen ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_UserProp( &propBuffer, NULL, &pUserPropKeyLen, &pUserPropVal, &pUserPropValLen ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_UserProp( &propBuffer, &pUserPropKey, NULL, &pUserPropVal, &pUserPropValLen ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_UserProp( &propBuffer, &pUserPropKey, &pUserPropKeyLen, NULL, &pUserPropValLen ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_UserProp( &propBuffer, &pUserPropKey, &pUserPropKeyLen, &pUserPropVal, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_UserProp( &propBuffer, &pUserPropKey, &pUserPropKeyLen, &pUserPropVal, &pUserPropValLen ) ); + break; + + case MQTT_REASON_STRING_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ReasonString( NULL, &pReasonString, &reasonStringLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ReasonString( &propBuffer1, &pReasonString, &reasonStringLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ReasonString( &propBuffer, NULL, &reasonStringLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ReasonString( &propBuffer, &pReasonString, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ReasonString( &propBuffer, &pReasonString, &reasonStringLength ) ); + break; + + case MQTT_SERVER_REF_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ServerRef( NULL, &pReasonString, &reasonStringLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ServerRef( &propBuffer1, &pReasonString, &reasonStringLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ServerRef( &propBuffer, NULL, &reasonStringLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ServerRef( &propBuffer, &pReasonString, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ServerRef( &propBuffer, &pReasonString, &reasonStringLength ) ); + break; + + case MQTT_SHARED_SUB_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnSharedSubAvailable( NULL, &sharedSubAvailable ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnSharedSubAvailable( &propBuffer1, &sharedSubAvailable ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnSharedSubAvailable( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnSharedSubAvailable( &propBuffer, &sharedSubAvailable ) ); + break; + + case MQTT_SERVER_KEEP_ALIVE_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnServerKeepAlive( NULL, &serverKeepAlive ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnServerKeepAlive( &propBuffer1, &serverKeepAlive ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnServerKeepAlive( &propBuffer, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnServerKeepAlive( &propBuffer, &serverKeepAlive ) ); + break; + + case MQTT_RESPONSE_INFO_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnResponseInfo( NULL, &pResponseInfo, &responseInfoLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnResponseInfo( &propBuffer1, &pResponseInfo, &responseInfoLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnResponseInfo( &propBuffer, NULL, &responseInfoLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnResponseInfo( &propBuffer, &pResponseInfo, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnResponseInfo( &propBuffer, &pResponseInfo, &responseInfoLength ) ); + break; + + case MQTT_AUTH_METHOD_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthMethod( NULL, &pAuthMethod, &authMethodLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthMethod( &propBuffer1, &pAuthMethod, &authMethodLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthMethod( &propBuffer, NULL, &authMethodLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthMethod( &propBuffer, &pAuthMethod, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnAuthMethod( &propBuffer, &pAuthMethod, &authMethodLength ) ); + break; + + case MQTT_AUTH_DATA_ID: + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthData( NULL, &pAuthData, &authDataLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthData( &propBuffer1, &pAuthData, &authDataLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthData( &propBuffer, NULL, &authDataLength ) ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, MQTTPropGet_ConnAuthData( &propBuffer, &pAuthData, NULL ) ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, MQTTPropGet_ConnAuthData( &propBuffer, &pAuthData, &authDataLength ) ); + break; + + default: + break; + } - /* Verify the password if provided. */ - if( pConnectInfo->pPassword != NULL ) - { - encodedStringLength = encodeString( encodedStringBuffer, - pConnectInfo->pPassword, - pConnectInfo->passwordLength ); - TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength ); - pIndex += encodedStringLength; + status = MQTT_IncomingGetNextProp( &propBuffer, &propertyId ); } -} -/** - * @brief Call Mqtt_SerializeConnect using NULL parameters and insufficient buffer - * size until we receive all possible MQTTBadParameter and MQTTNoMemory errors. - */ -void test_MQTT_SerializeConnect_Invalid_Params() + propBuffer.bufferLength = 10; + status = MQTT_IncomingGetNextProp( &propBuffer, &propertyId ); + TEST_ASSERT_EQUAL_INT( MQTTEndOfProperties, status ); +} +void test_getProps_decodeFailure( void ) { - MQTTStatus_t mqttStatus = MQTTSuccess; - size_t remainingLength = 0UL, packetSize = 0UL; - MQTTFixedBuffer_t networkBuffer = { 0 }; - MQTTConnectInfo_t connectInfo; + MQTTStatus_t status = MQTTSuccess; + MQTTPropBuilder_t propBuffer; + uint16_t twoByteProperty; + uint8_t oneByteProperty; + uint8_t propertyId; + uint32_t messageExpiry; + uint32_t sessionExpiry; + uint32_t maxPacketSize; + const char * string; + uint16_t stringLength; + + uint8_t buffer[ 500 ] = { 0 }; + size_t bufLength = 500; + + propBuffer.pBuffer = buffer; + propBuffer.bufferLength = bufLength; + propBuffer.currentIndex = 0; + propBuffer.fieldSet = 0; + + uint8_t * pIndex = buffer; + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + propBuffer.bufferLength = 2; + + status = MQTT_IncomingGetNextProp( &propBuffer, &propertyId ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + status = MQTTPropGet_PubTopicAlias( &propBuffer, &twoByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Test NULL pConnectInfo. */ - mqttStatus = MQTT_SerializeConnect( NULL, NULL, - remainingLength, &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + propBuffer.bufferLength += 1; + status = MQTT_IncomingGetNextProp( &propBuffer, &propertyId ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + status = MQTTPropGet_PubPayloadFormatIndicator( &propBuffer, &oneByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Test NULL pBuffer. */ - mqttStatus = MQTT_SerializeConnect( &connectInfo, NULL, - remainingLength, NULL ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + status = MQTTPropGet_PubMessageExpiryInterval( &propBuffer, &messageExpiry ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Test connectPacketSize > pBuffer->size. */ - /* Get MQTT connect packet size and remaining length. */ - setupConnectInfo( &connectInfo ); - mqttStatus = MQTT_GetConnectPacketSize( &connectInfo, - NULL, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - networkBuffer.pBuffer = mqttBuffer; - networkBuffer.size = packetSize - 1; - mqttStatus = MQTT_SerializeConnect( &connectInfo, NULL, - remainingLength, &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTNoMemory, mqttStatus ); -} + status = MQTTPropGet_PubContentType( &propBuffer, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); -/** - * @brief This method calls MQTT_SerializeConnect successfully using different parameters - * until we have full coverage on the private method, serializeConnectPacket(...). - */ -void test_MQTT_SerializeConnect_Happy_Paths() -{ - MQTTStatus_t mqttStatus = MQTTSuccess; - size_t remainingLength = 0; - size_t packetSize = 0; - MQTTFixedBuffer_t networkBuffer; - MQTTConnectInfo_t connectInfo; - MQTTPublishInfo_t willInfo; + status = MQTTPropGet_SessionExpiry( &propBuffer, &sessionExpiry ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Fill structs to pass into methods to be tested. */ - setupNetworkBuffer( &networkBuffer ); - setupConnectInfo( &connectInfo ); - setupPublishInfo( &willInfo ); - willInfo.dup = true; - willInfo.retain = true; + status = MQTTPropGet_ConnTopicAliasMax( &propBuffer, &twoByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Get MQTT connect packet size and remaining length. */ - mqttStatus = MQTT_GetConnectPacketSize( &connectInfo, - &willInfo, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - /* Make sure buffer has enough space. */ - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size ); - mqttStatus = MQTT_SerializeConnect( &connectInfo, &willInfo, - remainingLength, &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - verifySerializedConnectPacket( &connectInfo, &willInfo, - remainingLength, &networkBuffer ); + status = MQTTPropGet_ConnReceiveMax( &propBuffer, &twoByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Repeat with MQTTQoS1. */ - willInfo.qos = MQTTQoS1; - mqttStatus = MQTT_GetConnectPacketSize( &connectInfo, - &willInfo, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - /* Make sure buffer has enough space. */ - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size ); - mqttStatus = MQTT_SerializeConnect( &connectInfo, &willInfo, - remainingLength, &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - verifySerializedConnectPacket( &connectInfo, &willInfo, - remainingLength, &networkBuffer ); + status = MQTTPropGet_ConnMaxQos( &propBuffer, &oneByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + status = MQTTPropGet_ConnRetainAvailable( &propBuffer, &oneByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Re-initialize objects for branch coverage. */ - willInfo.pPayload = MQTT_SAMPLE_PAYLOAD; - willInfo.payloadLength = MQTT_SAMPLE_PAYLOAD_LEN; - willInfo.pTopicName = MQTT_CLIENT_IDENTIFIER; - willInfo.topicNameLength = MQTT_CLIENT_IDENTIFIER_LEN; - willInfo.dup = true; - willInfo.qos = MQTTQoS2; - willInfo.retain = false; - connectInfo.cleanSession = false; - connectInfo.pClientIdentifier = MQTT_CLIENT_IDENTIFIER; - connectInfo.clientIdentifierLength = MQTT_CLIENT_IDENTIFIER_LEN; - connectInfo.pUserName = NULL; - connectInfo.userNameLength = 0; - connectInfo.pPassword = NULL; - connectInfo.passwordLength = 0; + status = MQTTPropGet_ConnMaxPacketSize( &propBuffer, &maxPacketSize ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - mqttStatus = MQTT_GetConnectPacketSize( &connectInfo, - NULL, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - /* Make sure buffer has enough space. */ - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size ); - mqttStatus = MQTT_SerializeConnect( &connectInfo, &willInfo, - remainingLength, &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - verifySerializedConnectPacket( &connectInfo, &willInfo, - remainingLength, &networkBuffer ); + status = MQTTPropGet_ConnClientId( &propBuffer, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + + status = MQTTPropGet_ConnWildcard( &propBuffer, &oneByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); + status = MQTTPropGet_ConnSubId( &propBuffer, &oneByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Repeat with NULL pWillInfo. */ - mqttStatus = MQTT_GetConnectPacketSize( &connectInfo, - NULL, - &remainingLength, - &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - /* Make sure buffer has enough space. */ - TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size ); - mqttStatus = MQTT_SerializeConnect( &connectInfo, NULL, - remainingLength, &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - verifySerializedConnectPacket( &connectInfo, NULL, - remainingLength, &networkBuffer ); -} + status = MQTTPropGet_UserProp( &propBuffer, &string, &stringLength, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); -/* ================== Testing MQTT_SerializeDisconnect ===================== */ + status = MQTTPropGet_ReasonString( &propBuffer, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); -/** - * @brief Call Mqtt_SerializeDisconnect using a NULL pBuffer and an insufficient - * buffer size in order to receive MQTTBadParameter and MQTTNoMemory errors. - */ -void test_MQTT_SerializeDisconnect_Invalid_Params() -{ - MQTTStatus_t mqttStatus = MQTTSuccess; - size_t packetSize = 0; - MQTTFixedBuffer_t networkBuffer; + status = MQTTPropGet_ServerRef( &propBuffer, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Test NULL pFixedBuffer. */ - mqttStatus = MQTT_SerializeDisconnect( NULL ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + status = MQTTPropGet_ConnSharedSubAvailable( &propBuffer, &oneByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Test a NULL pFixedBuffer->pBuffer. */ - networkBuffer.pBuffer = NULL; - mqttStatus = MQTT_SerializeDisconnect( &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + status = MQTTPropGet_ConnServerKeepAlive( &propBuffer, &twoByteProperty ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Test disconnectPacketSize > pFixedBuffer->size. */ - /* Get MQTT disconnect packet size and remaining length. */ - mqttStatus = MQTT_GetDisconnectPacketSize( &packetSize ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - networkBuffer.pBuffer = mqttBuffer; - networkBuffer.size = packetSize - 1; - mqttStatus = MQTT_SerializeDisconnect( &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTNoMemory, mqttStatus ); -} + status = MQTTPropGet_ConnResponseInfo( &propBuffer, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); -/** - * @brief This method calls MQTT_SerializeDisconnect successfully in order to - * get full coverage on the method. - */ -void test_MQTT_SerializeDisconnect_Happy_Path() -{ - MQTTStatus_t mqttStatus = MQTTSuccess; - MQTTFixedBuffer_t networkBuffer; + status = MQTTPropGet_ConnAuthMethod( &propBuffer, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Fill structs to pass into methods to be tested. */ - setupNetworkBuffer( &networkBuffer ); + status = MQTTPropGet_ConnAuthData( &propBuffer, &string, &stringLength ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); - /* Make sure buffer has enough space. */ - mqttStatus = MQTT_SerializeDisconnect( &networkBuffer ); - TEST_ASSERT_EQUAL( MQTT_PACKET_TYPE_DISCONNECT, networkBuffer.pBuffer[ 0 ] ); - TEST_ASSERT_EQUAL( MQTT_DISCONNECT_REMAINING_LENGTH, networkBuffer.pBuffer[ 1 ] ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + size_t subId; + pIndex = buffer; + encodeRemainingLength( pIndex, 20971556356235 ); + status = MQTTPropGet_PubSubscriptionId( &propBuffer, &subId ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); } /* ================== Testing MQTT_UpdateDuplicatePublishFlag ===================== */ @@ -2919,3 +5055,209 @@ void test_MQTT_UpdateDuplicatePublishFlag_Happy_Path() } /* ========================================================================== */ + +void test_ValidatePublishProperties( void ) +{ + MQTTStatus_t status = MQTTSuccess; + uint16_t topicAlias; + uint16_t serverTopicAliasMax = 10; + + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, NULL, &topicAlias ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + MQTTPropBuilder_t propBuilder = { 0 }; + propBuilder.pBuffer = NULL; + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + uint8_t buf[ 50 ]; + propBuilder.pBuffer = buf; + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + uint8_t * pIndex = buf; + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + pIndex = serializeutf_8pair( pIndex ); + propBuilder.currentIndex = 20; + propBuilder.bufferLength = 50; + + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + pIndex = serializeuint_8( pIndex, MQTT_REQUEST_PROBLEM_ID ); + propBuilder.currentIndex += 2; + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + pIndex = buf; + propBuilder.currentIndex = 1; + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + + pIndex = buf; + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + status = MQTT_ValidatePublishProperties( serverTopicAliasMax, &propBuilder, &topicAlias ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); +} + +void test_ValidateDisconnectProperties( void ) +{ + MQTTStatus_t status = MQTTSuccess; + + status = MQTT_ValidateDisconnectProperties( 0, NULL ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t propBuffer; + propBuffer.pBuffer = NULL; + status = MQTT_ValidateDisconnectProperties( 0, &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + uint8_t buf[ 50 ]; + propBuffer.pBuffer = buf; + propBuffer.currentIndex = 25; + + uint8_t * pIndex = buf; + pIndex = serializeuint_32( pIndex, MQTT_SESSION_EXPIRY_ID ); + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + pIndex = serializeutf_8pair( pIndex ); + pIndex = serializeuint_16( pIndex, MQTT_TOPIC_ALIAS_ID ); + + status = MQTT_ValidateDisconnectProperties( 10, &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + status = MQTT_ValidateDisconnectProperties( 0, &propBuffer ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + propBuffer.currentIndex = 28; + status = MQTT_ValidateDisconnectProperties( 10, &propBuffer ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + propBuffer.currentIndex = 2; + status = MQTT_ValidateDisconnectProperties( 10, &propBuffer ); + TEST_ASSERT_EQUAL( MQTTBadResponse, status ); + + buf[ 0 ] = MQTT_SESSION_EXPIRY_ID; + buf[ 1 ] = 0, buf[ 2 ] = 0, buf[ 3 ] = 0, buf[ 4 ] = 0; + propBuffer.currentIndex = 5; + status = MQTT_ValidateDisconnectProperties( 0, &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); +} + +void test_ValidateUnsubscribeProperties( void ) +{ + MQTTStatus_t status = MQTTSuccess; + + status = MQTT_ValidateUnsubscribeProperties( NULL ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t propBuffer; + propBuffer.pBuffer = NULL; + status = MQTT_ValidateUnsubscribeProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + uint8_t buf[ 50 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = 50; + propBuffer.currentIndex = 13; + + uint8_t * pIndex = buf; + pIndex = serializeutf_8pair( pIndex ); + pIndex = serializeuint_32( pIndex, MQTT_SESSION_EXPIRY_ID ); + + status = MQTT_ValidateUnsubscribeProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + propBuffer.currentIndex = 18; + + status = MQTT_ValidateUnsubscribeProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); +} + +void test_ValidateWillProperties( void ) +{ + MQTTStatus_t status = MQTTSuccess; + + status = MQTT_ValidateWillProperties( NULL ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t propBuffer; + propBuffer.pBuffer = NULL; + status = MQTT_ValidateWillProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + uint8_t buf[ 50 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = 50; + propBuffer.currentIndex = 32; + + uint8_t * pIndex = buf; + pIndex = serializeutf_8pair( pIndex ); + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + pIndex = serializeuint_32( pIndex, MQTT_WILL_DELAY_ID ); + pIndex = serializeuint_8( pIndex, MQTT_PAYLOAD_FORMAT_ID ); + pIndex = serializeutf_8( pIndex, MQTT_CONTENT_TYPE_ID ); + pIndex = serializeuint_32( pIndex, MQTT_SESSION_EXPIRY_ID ); + + status = MQTT_ValidateWillProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + propBuffer.currentIndex = 37; + + status = MQTT_ValidateWillProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); +} + +void test_ValidatePublishAckProperties( void ) +{ + MQTTStatus_t status = MQTTSuccess; + + status = MQTT_ValidatePublishAckProperties( NULL ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t propBuffer; + propBuffer.pBuffer = NULL; + status = MQTT_ValidatePublishAckProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + uint8_t buf[ 50 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = 50; + propBuffer.currentIndex = 20; + + uint8_t * pIndex = buf; + pIndex = serializeutf_8pair( pIndex ); + pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID ); + pIndex = serializeuint_32( pIndex, MQTT_MSG_EXPIRY_ID ); + + status = MQTT_ValidatePublishAckProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + propBuffer.currentIndex = 25; + + status = MQTT_ValidatePublishAckProperties( &propBuffer ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); +} + +void test_Mqtt_PropertyBuilder_Init( void ) +{ + MQTTPropBuilder_t ackPropsBuilder; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + MQTTStatus_t mqttStatus; + + mqttStatus = MQTT_PropertyBuilder_Init( &( ackPropsBuilder ), ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + mqttStatus = MQTT_PropertyBuilder_Init( NULL, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTT_PropertyBuilder_Init( &( ackPropsBuilder ), NULL, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + + mqttStatus = MQTT_PropertyBuilder_Init( &( ackPropsBuilder ), ackPropsBuf, 0 ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); +} + +/* ========================================================================== */ diff --git a/test/unit-test/core_mqtt_state_utest.c b/test/unit-test/core_mqtt_state_utest.c index fd04d1523..bcd1ce9f7 100644 --- a/test/unit-test/core_mqtt_state_utest.c +++ b/test/unit-test/core_mqtt_state_utest.c @@ -94,11 +94,17 @@ static uint32_t getTime( void ) */ static void eventCallback( MQTTContext_t * pContext, MQTTPacketInfo_t * pPacketInfo, - MQTTDeserializedInfo_t * pDeserializedInfo ) + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode_t * pReasonCode, + MQTTPropBuilder_t * sendPropsBuffer, + MQTTPropBuilder_t * getPropsBuffer ) { ( void ) pContext; ( void ) pPacketInfo; ( void ) pDeserializedInfo; + ( void ) pReasonCode; + ( void ) sendPropsBuffer; + ( void ) getPropsBuffer; } static void resetPublishRecords( MQTTContext_t * pMqttContext ) @@ -171,14 +177,15 @@ void test_MQTT_ReserveState( void ) MQTTPubAckInfo_t incomingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; MQTTPubAckInfo_t outgoingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; - + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); status = MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); status = MQTT_InitStatefulQoS( &mqttContext, outgoingRecords, MQTT_STATE_ARRAY_MAX_COUNT, - incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT ); + incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); /* QoS 0 returns success. */ @@ -379,13 +386,16 @@ void test_MQTT_ReserveState_compactRecords( void ) MQTTPubAckInfo_t incomingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; MQTTPubAckInfo_t outgoingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + status = MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); status = MQTT_InitStatefulQoS( &mqttContext, outgoingRecords, MQTT_STATE_ARRAY_MAX_COUNT, - incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT ); + incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); /* Consider the state of the array with 2 states. 1 indicates a non empty @@ -555,13 +565,16 @@ void test_MQTT_UpdateStatePublish( void ) MQTTPubAckInfo_t incomingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; MQTTPubAckInfo_t outgoingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + status = MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); status = MQTT_InitStatefulQoS( &mqttContext, outgoingRecords, MQTT_STATE_ARRAY_MAX_COUNT, - incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT ); + incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); /* QoS 0. */ @@ -764,14 +777,15 @@ void test_MQTT_UpdateStateAck( void ) MQTTPubAckInfo_t incomingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; MQTTPubAckInfo_t outgoingRecords[ MQTT_STATE_ARRAY_MAX_COUNT ] = { 0 }; - + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); status = MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); status = MQTT_InitStatefulQoS( &mqttContext, outgoingRecords, MQTT_STATE_ARRAY_MAX_COUNT, - incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT ); + incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); /* NULL parameters. */ @@ -959,13 +973,16 @@ void test_MQTT_AckToResend( void ) transport.recv = transportRecvSuccess; transport.send = transportSendSuccess; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + status = MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); status = MQTT_InitStatefulQoS( &mqttContext, outgoingRecords, MQTT_STATE_ARRAY_MAX_COUNT, - incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT ); + incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); /* Invalid parameters. */ @@ -1054,14 +1071,15 @@ void test_MQTT_PublishToResend( void ) transport.send = transportSendSuccess; MQTTFixedBuffer_t networkBuffer = { 0 }; - + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); status = MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); status = MQTT_InitStatefulQoS( &mqttContext, outgoingRecords, MQTT_STATE_ARRAY_MAX_COUNT, - incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT ); + incomingRecords, MQTT_STATE_ARRAY_MAX_COUNT, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); diff --git a/test/unit-test/core_mqtt_utest.c b/test/unit-test/core_mqtt_utest.c index ad3e147da..b3181654f 100644 --- a/test/unit-test/core_mqtt_utest.c +++ b/test/unit-test/core_mqtt_utest.c @@ -33,19 +33,24 @@ #include "unity.h" /* Include paths for public enums, structures, and macros. */ -#include "core_mqtt.h" + +/* #include "mock_core_mqttv5_serializer.h" */ +/* #include "mock_core_mqttv5_state.h" */ #include "mock_core_mqtt_serializer.h" #include "mock_core_mqtt_state.h" - #include "core_mqtt_config_defaults.h" +#include "core_mqtt.h" + /* Set network context to double pointer to buffer (uint8_t**). */ struct NetworkContext { uint8_t ** buffer; }; +static MQTTStatus_t validateSubscribeReturn = MQTTSuccess; + /** * @brief MQTT client identifier. */ @@ -167,6 +172,16 @@ struct NetworkContext */ #define MQTT_SAMPLE_TOPIC_FILTER_LENGTH3 ( sizeof( MQTT_SAMPLE_TOPIC_FILTER3 ) - 1 ) + +#define TEST_TOPIC_ALIAS ( 2U ) +#define TEST_MSG_EXPIRY ( 100U ) +#define TEST_CONTENT_TYPE_LENGTH ( 2 ) +#define TEST_CONTENT_TYPE ( "ab" ) +#define TEST_RESPONSE_TOPIC_LENGTH ( 10 ) +#define TEST_RESPONSE_TOPIC ( "aaaaaaaaaa" ) +#define TEST_CORRELATION_DATA_LENGTH ( 5 ) +#define TEST_CORRELATION_DATA ( "abcde" ) + /** * @brief Return values of mocked calls in MQTT_ProcessLoop(). Used by * `expectProcessLoopCalls` @@ -193,19 +208,6 @@ struct MQTTVec size_t vectorLen; /**< Length of the transport vector. USER SHOULD NOT ACCESS THIS DIRECTLY - IT IS AN INTERNAL DETAIL AND CAN CHANGE. */ }; -/** - * @brief The packet type to be received by the process loop. - * IMPORTANT: Make sure this is set before calling expectProcessLoopCalls(...). - */ -static uint8_t currentPacketType = MQTT_PACKET_TYPE_INVALID; - -/** - * @brief The return value of modifyIncomingPacket(...) CMock callback that - * replaces a call to MQTT_GetIncomingPacketTypeAndLength. - * IMPORTANT: Make sure this is set before calling expectProcessLoopCalls(...). - */ -static MQTTStatus_t modifyIncomingPacketStatus = MQTTSuccess; - /** * @brief Time at the beginning of each test. Note that this is not updated with * a real clock. Instead, we simply increment this variable. @@ -233,6 +235,7 @@ static size_t publishCopyBufferSize = 0; */ static bool isEventCallbackInvoked = false; static bool receiveOnce = false; +static MQTTStatus_t modifyIncomingPacketStatus = MQTTSuccess; static const uint8_t SubscribeHeader[] = { @@ -252,6 +255,7 @@ static const uint8_t UnsubscribeHeader[] = }; static const size_t UnsubscribeHeaderLength = 5U; + /* ============================ UNITY FIXTURES ============================ */ /* Called before each test method. */ @@ -280,9 +284,6 @@ int suiteTearDown( int numFailures ) return numFailures; } - -/* ========================================================================== */ - /** * @brief Mock successful transport send, and write data into buffer for * verification. @@ -308,35 +309,7 @@ static int32_t mockSend( NetworkContext_t * pNetworkContext, return bytesToSend; } -/** - * @brief Initialize pNetworkBuffer using static buffer. - * - * @param[in] pNetworkBuffer Network buffer provided for the context. - */ -static void setupNetworkBuffer( MQTTFixedBuffer_t * const pNetworkBuffer ) -{ - pNetworkBuffer->pBuffer = mqttBuffer; - pNetworkBuffer->size = MQTT_TEST_BUFFER_LENGTH; -} - -/** - * @brief Mocked MQTT event callback. - * - * @param[in] pContext MQTT context pointer. - * @param[in] pPacketInfo Packet Info pointer for the incoming packet. - * @param[in] pDeserializedInfo Deserialized information from the incoming packet. - */ -static void eventCallback( MQTTContext_t * pContext, - MQTTPacketInfo_t * pPacketInfo, - MQTTDeserializedInfo_t * pDeserializedInfo ) -{ - ( void ) pContext; - ( void ) pPacketInfo; - ( void ) pDeserializedInfo; - - /* Update the global state to indicate that event callback is invoked. */ - isEventCallbackInvoked = true; -} +/* ========================================================================== */ /** * @brief A mocked timer query function that increments on every call. This @@ -378,16 +351,203 @@ static uint32_t getTimeMockBigTimeStep( void ) return globalEntryTime; } +/** + * @brief Mocked MQTT event callback. + * + * @param[in] pContext MQTT context pointer. + * @param[in] pPacketInfo Packet Info pointer for the incoming packet. + * @param[in] pDeserializedInfo Deserialized information from the incoming packet. + */ +static void eventCallback( MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode_t * pReasonCode, + MQTTPropBuilder_t * sendPropsBuffer, + MQTTPropBuilder_t * getPropsBuffer ) +{ + ( void ) pContext; + ( void ) pPacketInfo; + ( void ) pDeserializedInfo; + ( void ) pReasonCode; + ( void ) sendPropsBuffer; + ( void ) getPropsBuffer; + + /* Update the global state to indicate that event callback is invoked. */ + isEventCallbackInvoked = true; + sendPropsBuffer->pBuffer = NULL; +} + +/** + * @brief Mocked MQTT event callback. + * + * @param[in] pContext MQTT context pointer. + * @param[in] pPacketInfo Packet Info pointer for the incoming packet. + * @param[in] pDeserializedInfo Deserialized information from the incoming packet. + */ + +MQTTReasonCodeInfo_t GlobalAckInfo; + + +static void eventCallback2( MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode_t * pReasonCode, + MQTTPropBuilder_t * sendPropsBuffer, + MQTTPropBuilder_t * getPropsBuffer ) +{ + ( void ) pContext; + ( void ) pPacketInfo; + ( void ) pDeserializedInfo; + ( void ) getPropsBuffer; + + + uint8_t buf[ 13 ]; + buf[ 0 ] = 0x26; + buf[ 1 ] = 0x00; + buf[ 2 ] = 0x02; + buf[ 3 ] = 'a'; + buf[ 4 ] = 'b'; + buf[ 5 ] = 0x00; + buf[ 6 ] = 0x02; + buf[ 7 ] = 'c'; + buf[ 8 ] = 'd'; + buf[ 9 ] = 0x1F; + buf[ 10 ] = 0x00; + buf[ 11 ] = 0x01; + buf[ 12 ] = 'e'; + + *pReasonCode = MQTT_REASON_PUBREC_SUCCESS; + sendPropsBuffer->pBuffer = buf; + sendPropsBuffer->currentIndex = 13; + isEventCallbackInvoked = true; +} +static void eventCallback3( MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode_t * pReasonCode, + MQTTPropBuilder_t * sendPropsBuffer, + MQTTPropBuilder_t * getPropsBuffer ) +{ + ( void ) pContext; + ( void ) pPacketInfo; + ( void ) pDeserializedInfo; + ( void ) pReasonCode; + ( void ) sendPropsBuffer; + ( void ) getPropsBuffer; + + isEventCallbackInvoked = true; + + *pReasonCode = MQTT_REASON_PUBREC_NOT_AUTHORIZED; + sendPropsBuffer->pBuffer = NULL; + sendPropsBuffer->currentIndex = 0; + pDeserializedInfo->packetIdentifier = 0; +} + +static void eventCallbackInvalidRC( MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode_t * pReasonCode, + MQTTPropBuilder_t * sendPropsBuffer, + MQTTPropBuilder_t * getPropsBuffer ) +{ + ( void ) pContext; + ( void ) pPacketInfo; + ( void ) pDeserializedInfo; + ( void ) pReasonCode; + ( void ) sendPropsBuffer; + ( void ) getPropsBuffer; + + isEventCallbackInvoked = true; + + *pReasonCode = MQTT_REASON_SUBACK_GRANTED_QOS1; + uint8_t buf[ 13 ]; + buf[ 0 ] = 0x26; + buf[ 1 ] = 0x00; + buf[ 2 ] = 0x02; + buf[ 3 ] = 'a'; + buf[ 4 ] = 'b'; + buf[ 5 ] = 0x00; + buf[ 6 ] = 0x02; + buf[ 7 ] = 'c'; + buf[ 8 ] = 'd'; + buf[ 9 ] = 0x1F; + buf[ 10 ] = 0x00; + buf[ 11 ] = 0x01; + buf[ 12 ] = 'e'; + + sendPropsBuffer->pBuffer = buf; + sendPropsBuffer->currentIndex = 13; + pDeserializedInfo->packetIdentifier = 0; +} /** - * @brief A mocked timer function that could be used on a device with no system - * time. + * @brief Mocked MQTT event callback. + * + * @param[in] pContext MQTT context pointer. + * @param[in] pPacketInfo Packet Info pointer for the incoming packet. + * @param[in] pDeserializedInfo Deserialized information from the incoming packet. */ -static uint32_t getTimeDummy( void ) +static void eventCallback4( MQTTContext_t * pContext, + MQTTPacketInfo_t * pPacketInfo, + MQTTDeserializedInfo_t * pDeserializedInfo, + MQTTSuccessFailReasonCode_t * pReasonCode, + MQTTPropBuilder_t * sendPropsBuffer, + MQTTPropBuilder_t * getPropsBuffer ) { - return 0; + ( void ) pContext; + ( void ) pPacketInfo; + ( void ) pDeserializedInfo; + ( void ) getPropsBuffer; + uint8_t buf[ 10 ]; + + /* Update the global state to indicate that event callback is invoked. */ + *pReasonCode = MQTT_REASON_PUBREC_IMPLEMENTATION_SPECIFIC_ERROR; + isEventCallbackInvoked = true; + sendPropsBuffer->pBuffer = buf; + sendPropsBuffer->currentIndex = 0; +} + +static uint8_t * MQTTV5_SerializeAckFixed_cb( uint8_t * pIndex, + uint8_t packetType, + uint16_t packetId, + size_t remainingLength, + MQTTSuccessFailReasonCode_t reasonCode, + int numcallbacks ) +{ + ( void ) packetType; + ( void ) packetId; + ( void ) remainingLength; + ( void ) reasonCode; + ( void ) numcallbacks; + + return pIndex; +} + +MQTTStatus_t MQTT_SerializeAck_StubSuccess( const MQTTFixedBuffer_t * pFixedBuffer, + uint8_t packetType, + uint16_t packetId ) +{ + ( void ) pFixedBuffer; + ( void ) packetType; + ( void ) packetId; + return MQTTSuccess; +} + +static uint8_t * MQTTV5_SerializeDisconnectFixed_cb( uint8_t * pIndex, + MQTTSuccessFailReasonCode_t reasonCode, + size_t remainingLength, + int numcallbacks ) +{ + ( void ) pIndex; + ( void ) reasonCode; + ( void ) remainingLength; + ( void ) numcallbacks; + + return pIndex; } + + /** * @brief Mocked successful transport writev. * @@ -554,7 +714,6 @@ void publishClearCallback( struct MQTTContext * pContext, ( void ) packetId; } - static void verifyEncodedTopicString( TransportOutVector_t * pIoVectorIterator, char * pTopicFilter, size_t topicFilterLength, @@ -646,7 +805,7 @@ static int32_t transportWritevSubscribeSuccess( NetworkContext_t * pNetworkConte /* The header. */ if( writeCount == 0 ) { - TEST_ASSERT_EQUAL( 4, vectorsToBeSent ); + TEST_ASSERT_EQUAL( 5, vectorsToBeSent ); buffer = pIoVectorIterator->iov_base; length = pIoVectorIterator->iov_len; @@ -654,6 +813,15 @@ static int32_t transportWritevSubscribeSuccess( NetworkContext_t * pNetworkConte TEST_ASSERT_EQUAL( length, SubscribeHeaderLength ); TEST_ASSERT_EQUAL_UINT8_ARRAY( ( uint8_t * ) SubscribeHeader, buffer, SubscribeHeaderLength ); + bytesToWrite += length; + pIoVectorIterator++; + /* Property Length */ + buffer = pIoVectorIterator[ 0 ].iov_base; + length = pIoVectorIterator[ 0 ].iov_len; + + TEST_ASSERT_EQUAL( 1, length ); + TEST_ASSERT_EQUAL_UINT8( 0x00, *buffer ); + bytesToWrite += length; pIoVectorIterator++; @@ -666,7 +834,7 @@ static int32_t transportWritevSubscribeSuccess( NetworkContext_t * pNetworkConte writeCount++; - pIoVectorIterator += 4; + pIoVectorIterator += 3; } else if( writeCount == 1 ) { @@ -735,7 +903,7 @@ static int32_t transportWritevUnsubscribeSuccess( NetworkContext_t * pNetworkCon /* The header. */ if( writeCount == 0 ) { - TEST_ASSERT_EQUAL( 5, vectorsToBeSent ); + TEST_ASSERT_EQUAL( 6, vectorsToBeSent ); buffer = pIoVectorIterator->iov_base; length = pIoVectorIterator->iov_len; @@ -746,6 +914,15 @@ static int32_t transportWritevUnsubscribeSuccess( NetworkContext_t * pNetworkCon bytesToWrite += length; pIoVectorIterator++; + buffer = pIoVectorIterator[ 0 ].iov_base; + length = pIoVectorIterator[ 0 ].iov_len; + + TEST_ASSERT_EQUAL( 1, length ); + TEST_ASSERT_EQUAL_UINT8( 0x00, *buffer ); + + bytesToWrite += length; + pIoVectorIterator++; + /* First topic filter. */ verifyEncodedTopicStringUnsubscribe( pIoVectorIterator, MQTT_SAMPLE_TOPIC_FILTER, @@ -858,6 +1035,7 @@ static int32_t transportSendFailure( NetworkContext_t * pNetworkContext, ( void ) pNetworkContext; ( void ) pBuffer; ( void ) bytesToWrite; + LogError( ( "transportSendFailure" ) ); return -1; } @@ -911,12 +1089,13 @@ static int32_t transportSendSucceedThenFailAfterConnect( NetworkContext_t * pNet counter++; - if( counter == 3 ) + if( counter == 4 ) { retVal = -1; counter = 0; } + LogError( ( "transportSendSucceedThenFailAfterConnect: %d", retVal ) ); return retVal; } @@ -1008,19 +1187,6 @@ static int32_t transportRecvOneByte( NetworkContext_t * pNetworkContext, return 1; } -/** - * @brief Mocked transport returning zero bytes to simulate reception - * of no data over network. - */ -static int32_t transportRecvNoData( NetworkContext_t * pNetworkContext, - void * pBuffer, - size_t bytesToRead ) -{ - ( void ) pNetworkContext; - ( void ) pBuffer; - ( void ) bytesToRead; - return 0; -} /** * @brief Initialize the transport interface with the mocked functions for @@ -1037,15 +1203,33 @@ static void setupTransportInterface( TransportInterface_t * pTransport ) } /** - * @brief Initialize pSubscribeInfo using test-defined macros. + * @brief Initialize pNetworkBuffer using static buffer. * - * @param[in] pSubscribeInfo Pointer to MQTT subscription info. + * @param[in] pNetworkBuffer Network buffer provided for the context. */ -static void setupSubscriptionInfo( MQTTSubscribeInfo_t * pSubscribeInfo ) +static void setupNetworkBuffer( MQTTFixedBuffer_t * const pNetworkBuffer ) { - pSubscribeInfo->qos = MQTTQoS1; - pSubscribeInfo->pTopicFilter = MQTT_SAMPLE_TOPIC_FILTER; - pSubscribeInfo->topicFilterLength = MQTT_SAMPLE_TOPIC_FILTER_LENGTH; + pNetworkBuffer->pBuffer = mqttBuffer; + pNetworkBuffer->size = MQTT_TEST_BUFFER_LENGTH; +} + +static int32_t transportRecvNoData( NetworkContext_t * pNetworkContext, + void * pBuffer, + size_t bytesToRead ) +{ + ( void ) pNetworkContext; + ( void ) pBuffer; + ( void ) bytesToRead; + return 0; +} + +/** + * @brief A mocked timer function that could be used on a device with no system + * time. + */ +static uint32_t getTimeDummy( void ) +{ + return 0; } /** @@ -1066,6 +1250,22 @@ static void resetProcessLoopParams( ProcessLoopReturns_t * pExpectParams ) pExpectParams->timeoutMs = MQTT_NO_TIMEOUT_MS; } +/** + * @brief create default ackPropsBuilder + * + * @param[out] pAckPropsBuilder ackPropsBuilder to initialize + */ +static void setupackPropsBuilder( MQTTPropBuilder_t * pAckPropsBuilder ) +{ + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + pAckPropsBuilder->pBuffer = ackPropsBuf; + pAckPropsBuilder->currentIndex = 0; + pAckPropsBuilder->bufferLength = ackPropsBufLength; + pAckPropsBuilder->fieldSet = 0; +} + /** * @brief create default context * @@ -1084,21 +1284,29 @@ static void setUPContext( MQTTContext_t * mqttContext ) memset( mqttContext, 0x0, sizeof( MQTTContext_t ) ); + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + mqttStatus = MQTT_Init( mqttContext, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + mqttContext->ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttStatus = MQTT_InitStatefulQoS( mqttContext, outgoingRecords, 10, incomingRecords, - 10 ); + 10, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); } +uint8_t currentPacketType; + /** * @brief This helper function is used to expect any calls from the process loop * to mocked functions belonging to an external header file. Its parameters @@ -1182,6 +1390,14 @@ static void expectProcessLoopCalls( MQTTContext_t * const pContext, MQTT_DeserializePublish_ReturnThruPtr_pPublishInfo( pPubInfo ); } } + else if( currentPacketType == MQTT_PACKET_TYPE_PINGRESP ) + { + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( deserializeStatus ); + } + else if( ( currentPacketType == MQTT_PACKET_TYPE_SUBACK ) || ( currentPacketType == MQTT_PACKET_TYPE_UNSUBACK ) ) + { + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( deserializeStatus ); + } else { MQTT_DeserializeAck_ExpectAnyArgsAndReturn( deserializeStatus ); @@ -1237,7 +1453,16 @@ static void expectProcessLoopCalls( MQTTContext_t * const pContext, /* Serialize the packet to be sent in response to the received packet. */ if( expectMoreCalls ) { - MQTT_SerializeAck_ExpectAnyArgsAndReturn( serializeStatus ); + if( ( updateStateStatus != MQTTStateCollision ) && ( pContext->ackPropsBuffer.pBuffer != NULL ) ) + { + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb ); + MQTT_GetAckPacketSize_ExpectAnyArgsAndReturn( serializeStatus ); + } + else + { + MQTT_SerializeAck_ExpectAnyArgsAndReturn( serializeStatus ); + } if( serializeStatus != MQTTSuccess ) { @@ -1383,7 +1608,6 @@ void test_MQTT_CheckConnectStatus_return_correct_status( void ) TEST_ASSERT_EQUAL( MQTTStatusDisconnectPending, mqttStatus ); } - /* ========================================================================== */ /** @@ -1465,7 +1689,7 @@ void test_MQTT_Connect_sendConnect_already_connected( void ) MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTStatusConnected, status ); } @@ -1498,7 +1722,7 @@ void test_MQTT_Connect_sendConnect_disconnect_pending( void ) MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTStatusDisconnectPending, status ); } @@ -1534,11 +1758,12 @@ void test_MQTT_Connect_sendConnect_writev_error( void ) MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } + /** * @brief Test MQTT_Connect, except for receiving the CONNACK. */ @@ -1555,7 +1780,7 @@ void test_MQTT_Connect_sendConnect1( void ) size_t packetSize; setupTransportInterface( &transport ); - transport.writev = transportWritevFail; + transport.writev = NULL; setupNetworkBuffer( &networkBuffer ); memset( &mqttContext, 0x0, sizeof( mqttContext ) ); @@ -1570,7 +1795,7 @@ void test_MQTT_Connect_sendConnect1( void ) MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -1608,7 +1833,7 @@ void test_MQTT_Connect_WillInfoWrong( void ) MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -1629,7 +1854,7 @@ void test_MQTT_Connect_sendConnect2( void ) size_t packetSize; setupTransportInterface( &transport ); - transport.writev = transportWritevFail; + transport.writev = NULL; setupNetworkBuffer( &networkBuffer ); memset( &mqttContext, 0x0, sizeof( mqttContext ) ); @@ -1650,9 +1875,8 @@ void test_MQTT_Connect_sendConnect2( void ) MQTT_GetConnectPacketSize_IgnoreArg_pRemainingLength(); MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -1674,7 +1898,7 @@ void test_MQTT_Connect_ProperWIllInfo( void ) MQTTPublishInfo_t willInfo; setupTransportInterface( &transport ); - transport.writev = transportWritevFail; + transport.writev = NULL; setupNetworkBuffer( &networkBuffer ); memset( &mqttContext, 0x0, sizeof( mqttContext ) ); @@ -1706,9 +1930,8 @@ void test_MQTT_Connect_ProperWIllInfo( void ) MQTT_GetConnectPacketSize_IgnoreArg_pRemainingLength(); MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -1730,7 +1953,7 @@ void test_MQTT_Connect_ProperWIllInfoWithNoPayload( void ) MQTTPublishInfo_t willInfo; setupTransportInterface( &transport ); - transport.writev = transportWritevFail; + transport.writev = NULL; setupNetworkBuffer( &networkBuffer ); memset( &mqttContext, 0x0, sizeof( mqttContext ) ); @@ -1760,9 +1983,8 @@ void test_MQTT_Connect_ProperWIllInfoWithNoPayload( void ) MQTT_GetConnectPacketSize_IgnoreArg_pRemainingLength(); MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -1785,7 +2007,7 @@ void test_MQTT_Connect_ProperWIllInfoWithPayloadButZeroLength( void ) MQTTPublishInfo_t willInfo; setupTransportInterface( &transport ); - transport.writev = transportWritevFail; + transport.writev = NULL; setupNetworkBuffer( &networkBuffer ); memset( &mqttContext, 0x0, sizeof( mqttContext ) ); @@ -1817,22 +2039,17 @@ void test_MQTT_Connect_ProperWIllInfoWithPayloadButZeroLength( void ) MQTT_GetConnectPacketSize_IgnoreArg_pRemainingLength(); MQTT_GetConnectPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetConnectPacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } -/** - * @brief Test MQTT_Connect, except for receiving the CONNACK. - */ -void test_MQTT_Connect_sendConnect3( void ) +void test_MQTT_Connect3() { MQTTContext_t mqttContext = { 0 }; MQTTConnectInfo_t connectInfo = { 0 }; - uint32_t timeout = 2; - bool sessionPresent; + bool sessionPresent = false; MQTTStatus_t status; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; @@ -1841,18 +2058,11 @@ void test_MQTT_Connect_sendConnect3( void ) setupNetworkBuffer( &networkBuffer ); memset( &mqttContext, 0x0, sizeof( mqttContext ) ); - MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - /* Empty connect info fails. */ + MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); +/* No connect Properties */ MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter ); - memset( &connectInfo, 0x0, sizeof( connectInfo ) ); - - status = MQTT_Connect( &mqttContext, - &connectInfo, - NULL, - timeout, - &sessionPresent ); - + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -1876,13 +2086,13 @@ void test_MQTT_Connect_sendConnect4( void ) MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); /* Check parameters */ - status = MQTT_Connect( NULL, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( NULL, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_Connect( &mqttContext, NULL, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, NULL, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, NULL ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, NULL, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -1918,7 +2128,7 @@ void test_MQTT_Connect_sendConnect5( void ) * is called. */ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); } @@ -1949,50 +2159,133 @@ void test_MQTT_Connect_sendConnect6( void ) MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( 2 ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); } -/** - * @brief Test CONNACK reception in MQTT_Connect. - */ -void test_MQTT_Connect_receiveConnack( void ) +void test_MQTT_Connect_error_path() { MQTTContext_t mqttContext = { 0 }; MQTTConnectInfo_t connectInfo = { 0 }; - uint32_t timeout = 0; - bool sessionPresent, sessionPresentExpected; + MQTTPublishInfo_t willInfo = { 0 }; + uint32_t timeout = 2; + bool sessionPresent; MQTTStatus_t status; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; - MQTTPacketInfo_t incomingPacket = { 0 }; setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - transport.recv = transportRecvFailure; - memset( &mqttContext, 0x0, sizeof( mqttContext ) ); - MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + memset( &willInfo, 0x0, sizeof( MQTTPublishInfo_t ) ); + memset( &connectInfo, 0x0, sizeof( MQTTConnectInfo_t ) ); - /* Everything before receiving the CONNACK should succeed. */ - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); - MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); + MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - /* Nothing received from transport interface. Set timeout to 2 for branch coverage. */ + /* With non-NULL Will. */ + mqttContext.connectStatus = MQTTNotConnected; + mqttContext.keepAliveIntervalSec = 0; + + MQTTPropBuilder_t propBuilder = { 0 }; + uint8_t buf[ 500 ]; + size_t bufLength = sizeof( buf ); + propBuilder.pBuffer = buf; + propBuilder.bufferLength = bufLength; + + propBuilder.currentIndex = 200; + + MQTTPropBuilder_t willPropsBuilder = { 0 }; + uint8_t wbuf[ 500 ]; + size_t wbufLength = sizeof( wbuf ); + willPropsBuilder.pBuffer = wbuf; + willPropsBuilder.bufferLength = wbufLength; + + willPropsBuilder.currentIndex = 10; + + willInfo.pTopicName = "test"; + willInfo.topicNameLength = 4; + willInfo.pPayload = "Payload"; + willInfo.payloadLength = 7; + + connectInfo.pUserName = "abcd"; + connectInfo.userNameLength = 3; + connectInfo.passwordLength = 4; + connectInfo.pPassword = "1234"; + mqttContext.transportInterface.send = transportSendSuccess; + MQTT_ValidateWillProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); + updateContextWithConnectProps_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, &propBuilder, &willPropsBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); + + + MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); + + MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); + + MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); + + MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); + + status = MQTT_Connect( NULL, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + willInfo.pTopicName = NULL; + mqttContext.transportInterface.send = transportSendSuccess; + MQTT_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); + + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +void test_MQTT_Connect_receiveConnack( void ) +{ + MQTTContext_t mqttContext = { 0 }; + MQTTConnectInfo_t connectInfo = { 0 }; + uint32_t timeout = 0; + bool sessionPresent, sessionPresentExpected; + MQTTStatus_t status; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTConnectProperties_t properties = { 0 }; + MQTTPacketInfo_t incomingPacket = { 0 }; + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + transport.recv = transportRecvFailure; + + memset( &mqttContext, 0x0, sizeof( mqttContext ) ); + memset( &properties, 0x0, sizeof( properties ) ); + + MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + /* Everything before receiving the CONNACK should succeed. */ + MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); + + /* Nothing received from transport interface. Set timeout to 2 for branch coverage. */ timeout = 2; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable ); MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTNoDataAvailable, status ); + /* Did not receive a CONNACK. */ incomingPacket.type = MQTT_PACKET_TYPE_PINGRESP; incomingPacket.remainingLength = 0; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); /* Transport receive failure when receiving rest of packet. */ @@ -2002,15 +2295,14 @@ void test_MQTT_Connect_receiveConnack( void ) mqttContext.transportInterface.recv = transportRecvFailure; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); - /* Bad response when deserializing CONNACK. */ mqttContext.transportInterface.recv = transportRecvSuccess; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTBadResponse ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); /* Test case when broker sends session present flag in response to a @@ -2022,7 +2314,7 @@ void test_MQTT_Connect_receiveConnack( void ) MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresentExpected ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); } @@ -2048,7 +2340,6 @@ void test_MQTT_Connect_receiveConnack_retries( void ) MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); /* Everything before receiving the CONNACK should succeed. */ - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); /* Test with retries. MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT is 2. @@ -2058,7 +2349,7 @@ void test_MQTT_Connect_receiveConnack_retries( void ) MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable ); MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTNoDataAvailable, status ); /* Did not receive a CONNACK. */ @@ -2066,7 +2357,7 @@ void test_MQTT_Connect_receiveConnack_retries( void ) incomingPacket.remainingLength = 0; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); /* Transport receive failure when receiving rest of packet. */ @@ -2074,7 +2365,7 @@ void test_MQTT_Connect_receiveConnack_retries( void ) incomingPacket.remainingLength = 2; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); /* Bad response when deserializing CONNACK. */ @@ -2082,14 +2373,10 @@ void test_MQTT_Connect_receiveConnack_retries( void ) MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTBadResponse ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); } -/** - * @brief Test error cases for MQTT_Connect when a timeout occurs or the packet - * needs to be discarded in MQTT_Connect. - */ void test_MQTT_Connect_partial_receive( void ) { MQTTContext_t mqttContext = { 0 }; @@ -2106,10 +2393,10 @@ void test_MQTT_Connect_partial_receive( void ) transport.recv = transportRecvNoData; memset( &mqttContext, 0x0, sizeof( mqttContext ) ); - MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + status = MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); /* Everything before receiving the CONNACK should succeed. */ - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); incomingPacket.type = MQTT_PACKET_TYPE_CONNACK; incomingPacket.remainingLength = 2; @@ -2119,7 +2406,8 @@ void test_MQTT_Connect_partial_receive( void ) MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); /* Update to use mock receive function that receives one byte at a time for the @@ -2132,7 +2420,7 @@ void test_MQTT_Connect_partial_receive( void ) incomingPacket.remainingLength = 3; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTNoDataAvailable, status ); /* Discard packet, no timeout provided. This should fail since multiple @@ -2141,7 +2429,7 @@ void test_MQTT_Connect_partial_receive( void ) mqttContext.transportInterface.recv = transportRecvSuccess; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); /* Timeout while discarding packet. */ @@ -2150,7 +2438,7 @@ void test_MQTT_Connect_partial_receive( void ) incomingPacket.remainingLength = 20; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); /* Receive failure while discarding packet. */ @@ -2159,7 +2447,7 @@ void test_MQTT_Connect_partial_receive( void ) mqttContext.getTime = getTimeDummy; MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, MQTT_NO_TIMEOUT_MS, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, MQTT_NO_TIMEOUT_MS, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status ); } @@ -2186,7 +2474,6 @@ void test_MQTT_Connect_resendPendingAcks( void ) memset( &connectInfo, 0x00, sizeof( connectInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; @@ -2203,7 +2490,7 @@ void test_MQTT_Connect_resendPendingAcks( void ) /* No packets to resend. */ MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); @@ -2222,7 +2509,7 @@ void test_MQTT_Connect_resendPendingAcks( void ) /* Serialize Ack failure. */ MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTBadParameter ); MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); TEST_ASSERT_EQUAL_INT( MQTTDisconnectPending, mqttContext.connectStatus ); TEST_ASSERT_TRUE( sessionPresentResult ); @@ -2240,7 +2527,7 @@ void test_MQTT_Connect_resendPendingAcks( void ) MQTT_PubrelToResend_ReturnThruPtr_pState( &pubRelState ); MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); TEST_ASSERT_EQUAL_INT( MQTTDisconnectPending, mqttContext.connectStatus ); TEST_ASSERT_TRUE( sessionPresentResult ); @@ -2260,7 +2547,7 @@ void test_MQTT_Connect_resendPendingAcks( void ) MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); /* Query for any remaining packets pending to ack. */ MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_ID_INVALID ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); @@ -2286,7 +2573,7 @@ void test_MQTT_Connect_resendPendingAcks( void ) MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTBadParameter ); /* Query for any remaining packets pending to ack. */ MQTT_PubrelToResend_ExpectAnyArgsAndReturn( packetIdentifier + 2 ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); TEST_ASSERT_EQUAL_INT( MQTTDisconnectPending, mqttContext.connectStatus ); @@ -2311,7 +2598,7 @@ void test_MQTT_Connect_resendPendingAcks( void ) MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); /* Query for any remaining packets pending to ack. */ MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_ID_INVALID ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); @@ -2333,6 +2620,8 @@ void test_MQTT_Connect_resendUnAckedPublishes( void ) MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; uint8_t * localPublishCopyBuffer = ( uint8_t * ) "Hello world!"; + uint8_t ackPropsBuf[ 100 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); publishCopyBuffer = localPublishCopyBuffer; publishCopyBufferSize = sizeof( "Hello world!" ); @@ -2343,16 +2632,15 @@ void test_MQTT_Connect_resendUnAckedPublishes( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &connectInfo, 0x00, sizeof( connectInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, publishClearCallback ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; @@ -2370,7 +2658,7 @@ void test_MQTT_Connect_resendUnAckedPublishes( void ) MQTT_PublishToResend_ExpectAnyArgsAndReturn( 1 ); MQTT_PublishToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); @@ -2390,6 +2678,8 @@ void test_MQTT_Connect_resendUnAckedPublishes2( void ) MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; uint8_t * localPublishCopyBuffer = ( uint8_t * ) "Hello world!"; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); publishCopyBuffer = localPublishCopyBuffer; publishCopyBufferSize = sizeof( "Hello world!" ); @@ -2400,16 +2690,15 @@ void test_MQTT_Connect_resendUnAckedPublishes2( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &connectInfo, 0x00, sizeof( connectInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, publishClearCallback ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; mqttContext.clearFunction = publishClearCallback; @@ -2429,7 +2718,7 @@ void test_MQTT_Connect_resendUnAckedPublishes2( void ) /* No publishes to resend. */ MQTT_PublishToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); @@ -2452,6 +2741,8 @@ void test_MQTT_Connect_resendUnAckedPublishes3( void ) MQTTPubAckInfo_t outgoingRecords = { 0 }; /* MQTTPublishState_t expectedState = { 0 }; */ uint8_t * localPublishCopyBuffer = ( uint8_t * ) "Hello world!"; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); publishCopyBuffer = localPublishCopyBuffer; publishCopyBufferSize = sizeof( "Hello world!" ); @@ -2462,16 +2753,15 @@ void test_MQTT_Connect_resendUnAckedPublishes3( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &connectInfo, 0x00, sizeof( connectInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, publishClearCallback ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; /* Test 4. One publish packet found to resend, but retrieve failed. */ @@ -2489,7 +2779,7 @@ void test_MQTT_Connect_resendUnAckedPublishes3( void ) MQTT_PublishToResend_ExpectAnyArgsAndReturn( packetIdentifier ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); mqttContext.retrieveFunction = publishRetrieveCallbackFailed; - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTPublishRetrieveFailed, status ); TEST_ASSERT_EQUAL_INT( MQTTDisconnectPending, mqttContext.connectStatus ); TEST_ASSERT_TRUE( sessionPresentResult ); @@ -2511,6 +2801,8 @@ void test_MQTT_Connect_resendUnAckedPublishes4( void ) MQTTPubAckInfo_t outgoingRecords = { 0 }; /* MQTTPublishState_t expectedState = { 0 }; */ uint8_t * localPublishCopyBuffer = ( uint8_t * ) "Hello world!"; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); publishCopyBuffer = localPublishCopyBuffer; publishCopyBufferSize = sizeof( "Hello world!" ); @@ -2521,16 +2813,15 @@ void test_MQTT_Connect_resendUnAckedPublishes4( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &connectInfo, 0x00, sizeof( connectInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, publishClearCallback ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; mqttContext.retrieveFunction = publishRetrieveCallbackSuccess; @@ -2551,7 +2842,7 @@ void test_MQTT_Connect_resendUnAckedPublishes4( void ) MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); MQTT_PublishToResend_ExpectAnyArgsAndReturn( packetIdentifier ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresentResult, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); TEST_ASSERT_EQUAL_INT( MQTTDisconnectPending, mqttContext.connectStatus ); TEST_ASSERT_TRUE( sessionPresentResult ); @@ -2571,6 +2862,8 @@ void test_MQTT_Connect_resendUnAckedPublishes5( void ) MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; uint8_t * localPublishCopyBuffer = ( uint8_t * ) "Hello world!"; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); publishCopyBuffer = localPublishCopyBuffer; publishCopyBufferSize = sizeof( "Hello world!" ); @@ -2581,16 +2874,15 @@ void test_MQTT_Connect_resendUnAckedPublishes5( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &connectInfo, 0x00, sizeof( connectInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, publishClearCallback ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; mqttContext.transportInterface.send = transportSendSuccess; @@ -2609,12 +2901,13 @@ void test_MQTT_Connect_resendUnAckedPublishes5( void ) MQTT_PublishToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); /* Query for any remaining packets pending to ack. */ - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); } + void test_MQTT_Connect_resendUnAckedPublishes6( void ) { MQTTContext_t mqttContext = { 0 }; @@ -2630,6 +2923,9 @@ void test_MQTT_Connect_resendUnAckedPublishes6( void ) MQTTPubAckInfo_t outgoingRecords = { 0 }; uint8_t * localPublishCopyBuffer = ( uint8_t * ) "Hello world!"; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + publishCopyBuffer = localPublishCopyBuffer; publishCopyBufferSize = sizeof( "Hello world!" ); @@ -2637,16 +2933,15 @@ void test_MQTT_Connect_resendUnAckedPublishes6( void ) setupNetworkBuffer( &networkBuffer ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccessThenFail, publishClearCallback ); - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; @@ -2667,15 +2962,11 @@ void test_MQTT_Connect_resendUnAckedPublishes6( void ) /* Second packet. */ MQTT_PublishToResend_ExpectAnyArgsAndReturn( packetIdentifier + 1 ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTPublishRetrieveFailed, status ); TEST_ASSERT_EQUAL_INT( MQTTDisconnectPending, mqttContext.connectStatus ); } -/** - * @brief Test success case for MQTT_Connect(). - */ - #define MQTT_STATE_ARRAY_MAX_COUNT 1 void test_MQTT_Connect_happy_path1() @@ -2701,17 +2992,45 @@ void test_MQTT_Connect_happy_path1() sessionPresent = false; incomingPacket.type = MQTT_PACKET_TYPE_CONNACK; incomingPacket.remainingLength = 2; - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_IgnoreAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); TEST_ASSERT_FALSE( mqttContext.waitingForPingResp ); + + /*Test MQTT_Connect with NULL buffer of propBuilder.*/ + mqttContext.connectStatus = MQTTNotConnected; + MQTTPropBuilder_t propBuilder; + propBuilder.pBuffer = NULL; + MQTTPublishInfo_t willInfo = { 0 }; + willInfo.pTopicName = "willTopic"; + willInfo.topicNameLength = 10; + MQTT_ValidateWillProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); + MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); + MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_IgnoreAndReturn( MQTTSuccess ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, 0U, &sessionPresent, &propBuilder, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + mqttContext.connectStatus = MQTTNotConnected; + uint8_t buf[ 10 ]; + incomingPacket.pRemainingData = buf; + incomingPacket.remainingLength = 2; + buf[ 1 ] = MQTT_REASON_CONNACK_IMPLEMENTATION_SPECIFIC_ERROR; + MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); + MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); + MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTServerRefused ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status ); } /** @@ -2735,7 +3054,6 @@ void test_MQTT_Connect_happy_path2() MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); connectInfo.keepAliveSeconds = MQTT_SAMPLE_KEEPALIVE_INTERVAL_S; - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); /* Success. */ @@ -2744,7 +3062,7 @@ void test_MQTT_Connect_happy_path2() MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_IgnoreAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); @@ -2782,14 +3100,13 @@ void test_MQTT_Connect_happy_path3() willInfo.pPayload = "Payload"; willInfo.payloadLength = 7; incomingPacket.type = MQTT_PACKET_TYPE_CONNACK; - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); @@ -2812,15 +3129,18 @@ void test_MQTT_Connect_happy_path4() MQTTPacketInfo_t incomingPacket = { 0 }; MQTTPubAckInfo_t incomingRecords[ 10 ] = { 0 }; MQTTPubAckInfo_t outgoingRecords[ 10 ] = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); memset( &mqttContext, 0x0, sizeof( mqttContext ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, outgoingRecords, 10, - incomingRecords, 10 ); + incomingRecords, 10, ackPropsBuf, ackPropsBufLength ); /* Request to establish a clean session. */ mqttContext.connectStatus = MQTTNotConnected; @@ -2840,14 +3160,13 @@ void test_MQTT_Connect_happy_path4() /* Set ping response flag to true to ensure it will be cleared. */ mqttContext.waitingForPingResp = true; - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresentExpected ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); TEST_ASSERT_EQUAL_INT( connectInfo.keepAliveSeconds, mqttContext.keepAliveIntervalSec ); @@ -2893,7 +3212,6 @@ void test_MQTT_Connect_happy_path5() incomingPacket.type = MQTT_PACKET_TYPE_CONNACK; - MQTT_SerializeConnect_IgnoreAndReturn( MQTTSuccess ); MQTT_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess ); MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb ); MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); @@ -2902,7 +3220,7 @@ void test_MQTT_Connect_happy_path5() MQTT_DeserializeAck_ReturnThruPtr_pSessionPresent( &sessionPresentExpected ); MQTT_PubrelToResend_ExpectAnyArgsAndReturn( MQTT_PACKET_TYPE_INVALID ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); @@ -2945,7 +3263,7 @@ void test_MQTT_Connect_happy_path6() MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_DeserializeAck_IgnoreAndReturn( MQTTSuccess ); - status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent ); + status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent, NULL, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); TEST_ASSERT_EQUAL_INT( MQTTConnected, mqttContext.connectStatus ); @@ -2962,9 +3280,12 @@ void test_MQTT_Publish( void ) { MQTTContext_t mqttContext = { 0 }; MQTTPublishInfo_t publishInfo = { 0 }; + MQTTConnectProperties_t properties = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; MQTTStatus_t status; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; const uint16_t PACKET_ID = 1; @@ -2974,10 +3295,60 @@ void test_MQTT_Publish( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); + memset( &properties, 0x0, sizeof( properties ) ); + + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_InitStatefulQoS( &mqttContext, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); - /* Verify parameters. */ - status = MQTT_Publish( NULL, &publishInfo, PACKET_ID ); + mqttContext.connectStatus = MQTTConnected; + + MQTTPropBuilder_t propBuilder = { 0 }; + uint8_t buf[ 500 ]; + size_t bufLength = sizeof( buf ); + propBuilder.pBuffer = buf; + propBuilder.bufferLength = bufLength; + + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTBadParameter ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + + publishInfo.qos = 0; + publishInfo.pTopicName = "ab"; + publishInfo.topicNameLength = 2; + publishInfo.retain = 1; + publishInfo.dup = 1; + publishInfo.pPayload = "Payload"; + publishInfo.payloadLength = 7; + + MQTT_ValidatePublishProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + propBuilder.pBuffer = NULL; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + propBuilder.pBuffer = buf; + + MQTT_ValidatePublishProperties_ExpectAnyArgsAndReturn( MQTTBadParameter ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, &propBuilder ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -3002,7 +3373,7 @@ void test_MQTT_Publish2( void ) memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - status = MQTT_Publish( &mqttContext, NULL, PACKET_ID ); + status = MQTT_Publish( &mqttContext, NULL, PACKET_ID, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -3026,7 +3397,7 @@ void test_MQTT_Publish3( void ) MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); publishInfo.qos = MQTTQoS1; - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -3053,7 +3424,7 @@ void test_MQTT_Publish4( void ) publishInfo.payloadLength = 1; publishInfo.pPayload = NULL; - status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -3070,7 +3441,7 @@ void test_MQTT_Publish5( void ) setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - transport.send = transportSendFailure; + /* transport.send = transportSendFailure; */ memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); @@ -3078,8 +3449,9 @@ void test_MQTT_Publish5( void ) /* Bad Parameter when getting packet size. */ publishInfo.qos = MQTTQoS0; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter ); - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -3103,9 +3475,10 @@ void test_MQTT_Publish6( void ) MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); /* Always return success from now on. */ + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTNoMemory ); - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status ); } @@ -3122,6 +3495,8 @@ void test_MQTT_Publish_Storing_Publish_Success( void ) MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; MQTTPublishState_t expectedState = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -3130,10 +3505,10 @@ void test_MQTT_Publish_Storing_Publish_Success( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, @@ -3144,7 +3519,7 @@ void test_MQTT_Publish_Storing_Publish_Success( void ) publishInfo.qos = MQTTQoS1; expectedState = MQTTPublishSend; - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); @@ -3157,7 +3532,7 @@ void test_MQTT_Publish_Storing_Publish_Success( void ) MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &expectedState ); mqttContext.transportInterface.send = transportSendSuccess; - status = MQTT_Publish( &mqttContext, &publishInfo, 1 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 1, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } @@ -3174,6 +3549,8 @@ void test_MQTT_Publish_Storing_Publish_Success_For_Duplicate_Publish( void ) MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; MQTTPublishState_t expectedState = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -3182,10 +3559,10 @@ void test_MQTT_Publish_Storing_Publish_Success_For_Duplicate_Publish( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, @@ -3197,7 +3574,7 @@ void test_MQTT_Publish_Storing_Publish_Success_For_Duplicate_Publish( void ) publishInfo.dup = true; expectedState = MQTTPublishSend; - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); @@ -3207,7 +3584,7 @@ void test_MQTT_Publish_Storing_Publish_Success_For_Duplicate_Publish( void ) MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &expectedState ); mqttContext.transportInterface.send = transportSendSuccess; - status = MQTT_Publish( &mqttContext, &publishInfo, 1 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 1, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } @@ -3223,6 +3600,8 @@ void test_MQTT_Publish_Storing_Publish_Failed( void ) MQTTStatus_t status; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -3231,10 +3610,10 @@ void test_MQTT_Publish_Storing_Publish_Failed( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackFailed, publishRetrieveCallbackSuccess, @@ -3243,7 +3622,7 @@ void test_MQTT_Publish_Storing_Publish_Failed( void ) mqttContext.connectStatus = MQTTConnected; publishInfo.qos = MQTTQoS1; - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); @@ -3252,7 +3631,7 @@ void test_MQTT_Publish_Storing_Publish_Failed( void ) MQTT_UpdateDuplicatePublishFlag_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttContext.transportInterface.send = transportSendSuccess; - status = MQTT_Publish( &mqttContext, &publishInfo, 1 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 1, NULL ); TEST_ASSERT_EQUAL_INT( MQTTPublishStoreFailed, status ); } @@ -3268,6 +3647,8 @@ void test_MQTT_Publish_Storing_Publish_Failed_Due_To_Dup_Flag_Not_Set( void ) MQTTStatus_t status; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -3277,9 +3658,10 @@ void test_MQTT_Publish_Storing_Publish_Failed_Due_To_Dup_Flag_Not_Set( void ) memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); MQTT_InitRetransmits( &mqttContext, publishStoreCallbackFailed, publishRetrieveCallbackSuccess, @@ -3288,7 +3670,7 @@ void test_MQTT_Publish_Storing_Publish_Failed_Due_To_Dup_Flag_Not_Set( void ) mqttContext.connectStatus = MQTTConnected; publishInfo.qos = MQTTQoS1; - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); @@ -3297,10 +3679,11 @@ void test_MQTT_Publish_Storing_Publish_Failed_Due_To_Dup_Flag_Not_Set( void ) MQTT_UpdateDuplicatePublishFlag_ExpectAnyArgsAndReturn( MQTTBadParameter ); mqttContext.transportInterface.send = transportSendSuccess; - status = MQTT_Publish( &mqttContext, &publishInfo, 1 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 1, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } + /** * @brief Test that MQTT_Publish works as intended. */ @@ -3322,6 +3705,7 @@ void test_MQTT_Publish_DuplicatePublish( void ) MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); /* Always return success from now on. */ + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTStateCollision ); @@ -3338,11 +3722,50 @@ void test_MQTT_Publish_DuplicatePublish( void ) publishInfo.pTopicName = "TestTopic"; publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); - status = MQTT_Publish( &mqttContext, &publishInfo, 10 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 10, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } +void test_MQTT_Publish_DuplicatePublish2( void ) +{ + MQTTContext_t mqttContext = { 0 }; + MQTTPublishInfo_t publishInfo = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTStatus_t status; + MQTTPubAckInfo_t outgoingPublishRecord[ 10 ]; + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + transport.send = transportSendFailure; + + memset( &mqttContext, 0x0, sizeof( mqttContext ) ); + memset( &publishInfo, 0x0, sizeof( publishInfo ) ); + MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + + /* Always return success from now on. */ + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTStateCollision ); + + mqttContext.outgoingPublishRecordMaxCount = 10; + mqttContext.outgoingPublishRecords = outgoingPublishRecord; + mqttContext.connectStatus = MQTTConnected; + + publishInfo.qos = MQTTQoS1; + publishInfo.dup = false; + publishInfo.pPayload = "TestPublish"; + publishInfo.payloadLength = strlen( publishInfo.pPayload ); + publishInfo.pTopicName = "TestTopic"; + publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); + + status = MQTT_Publish( &mqttContext, &publishInfo, 10, NULL ); + + TEST_ASSERT_EQUAL_INT( MQTTStateCollision, status ); +} + /** * @brief Test that MQTT_Publish works as intended when the connection status is anything but MQTTConnected. */ @@ -3364,16 +3787,18 @@ void test_MQTT_Publish_not_connected( void ) /* Test 1 connection status is MQTTNotConnected */ mqttContext.connectStatus = MQTTNotConnected; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Publish( &mqttContext, &publishInfo, 10 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 10, NULL ); TEST_ASSERT_EQUAL_INT( MQTTStatusNotConnected, status ); /* Test 2 connection status is MQTTDisconnectPending */ mqttContext.connectStatus = MQTTDisconnectPending; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Publish( &mqttContext, &publishInfo, 10 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 10, NULL ); TEST_ASSERT_EQUAL_INT( MQTTStatusDisconnectPending, status ); } @@ -3396,8 +3821,10 @@ void test_MQTT_Publish_DuplicatePublish_UpdateFailed( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + mqttContext.connectStatus = MQTTConnected; /* Always return success from now on. */ + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTStateCollision ); @@ -3405,7 +3832,6 @@ void test_MQTT_Publish_DuplicatePublish_UpdateFailed( void ) mqttContext.outgoingPublishRecordMaxCount = 10; mqttContext.outgoingPublishRecords = outgoingPublishRecord; - mqttContext.connectStatus = MQTTConnected; publishInfo.qos = MQTTQoS1; publishInfo.dup = true; @@ -3414,7 +3840,7 @@ void test_MQTT_Publish_DuplicatePublish_UpdateFailed( void ) publishInfo.pTopicName = "TestTopic"; publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); - status = MQTT_Publish( &mqttContext, &publishInfo, 10 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 10, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -3441,6 +3867,7 @@ void test_MQTT_Publish_WriteVSendsPartialBytes( void ) MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); /* Always return success from now on. */ + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ReturnThruPtr_headerSize( &headerLen ); @@ -3458,14 +3885,11 @@ void test_MQTT_Publish_WriteVSendsPartialBytes( void ) publishInfo.pTopicName = "TestTopic"; publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); - status = MQTT_Publish( &mqttContext, &publishInfo, 10 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 10, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } -/** - * @brief Test that MQTT_Publish works as intended. - */ void test_MQTT_Publish7( void ) { MQTTContext_t mqttContext = { 0 }; @@ -3476,12 +3900,13 @@ void test_MQTT_Publish7( void ) setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - transport.writev = transportWritevFail; + transport.writev = NULL; + transport.send = transportSendFailure; memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttContext.connectStatus = MQTTConnected; @@ -3491,7 +3916,7 @@ void test_MQTT_Publish7( void ) * publish header. */ publishInfo.pPayload = "Test"; publishInfo.payloadLength = 4; - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -3508,23 +3933,23 @@ void test_MQTT_Publish8( void ) setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - transport.writev = transportWritevFail; + transport.writev = NULL; memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - mqttContext.connectStatus = MQTTConnected; - /* We want to test the first call to sendPacket within sendPublish succeeding, * and the second one failing. */ mqttContext.transportInterface.send = transportSendSucceedThenFail; + + mqttContext.connectStatus = MQTTConnected; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); - publishInfo.pPayload = "Test"; publishInfo.payloadLength = 4; - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -3548,12 +3973,12 @@ void test_MQTT_Publish9( void ) MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); mqttContext.connectStatus = MQTTConnected; - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttContext.transportInterface.send = transportSendSuccess; - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } @@ -3581,10 +4006,11 @@ void test_MQTT_Publish10( void ) /* Test that sending a publish without a payload succeeds. */ publishInfo.pPayload = NULL; publishInfo.payloadLength = 0; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } @@ -3616,7 +4042,7 @@ void test_MQTT_Publish11( void ) /* Now for non zero QoS, which uses state engine. */ publishInfo.qos = MQTTQoS2; - status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, NULL ); TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } @@ -3632,7 +4058,8 @@ void test_MQTT_Publish12( void ) MQTTFixedBuffer_t networkBuffer = { 0 }; MQTTStatus_t status; MQTTPublishState_t expectedState = { 0 }; - + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; @@ -3644,13 +4071,14 @@ void test_MQTT_Publish12( void ) memset( &mqttContext, 0x0, sizeof( mqttContext ) ); memset( &publishInfo, 0x0, sizeof( publishInfo ) ); - MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + MQTT_Init( &mqttContext, &transport, getTime, eventCallback2, &networkBuffer ); publishInfo.qos = MQTTQoS1; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &mqttContext, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); mqttContext.outgoingPublishRecords[ 0 ].packetId = 1; mqttContext.outgoingPublishRecords[ 0 ].qos = MQTTQoS2; @@ -3662,57 +4090,18 @@ void test_MQTT_Publish12( void ) mqttContext.connectStatus = MQTTConnected; expectedState = MQTTPublishSend; - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &expectedState ); - status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } -/** - * @brief Test that MQTT_Publish works as intended. - */ -void test_MQTT_Publish13( void ) -{ - MQTTContext_t mqttContext = { 0 }; - MQTTPublishInfo_t publishInfo = { 0 }; - TransportInterface_t transport = { 0 }; - MQTTFixedBuffer_t networkBuffer = { 0 }; - MQTTStatus_t status; - MQTTPubAckInfo_t incomingRecords = { 0 }; - MQTTPubAckInfo_t outgoingRecords = { 0 }; - - const uint16_t PACKET_ID = 1; - - setupTransportInterface( &transport ); - setupNetworkBuffer( &networkBuffer ); - transport.send = transportSendFailure; - - memset( &mqttContext, 0x0, sizeof( mqttContext ) ); - memset( &publishInfo, 0x0, sizeof( publishInfo ) ); - MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - - mqttContext.connectStatus = MQTTConnected; - - MQTT_InitStatefulQoS( &mqttContext, - &outgoingRecords, 4, - &incomingRecords, 4 ); - - publishInfo.qos = MQTTQoS1; - - MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); - /* Duplicate publish. dup flag is not marked by application. */ - MQTT_ReserveState_ExpectAnyArgsAndReturn( MQTTStateCollision ); - status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID ); - TEST_ASSERT_EQUAL_INT( MQTTStateCollision, status ); -} - /** * @brief Test that MQTT_Publish works as intended. */ @@ -3738,11 +4127,11 @@ void test_MQTT_Publish14( void ) /* Duplicate publish. dup flag is marked by application. */ publishInfo.dup = true; - + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } @@ -3771,10 +4160,11 @@ void test_MQTT_Publish15( void ) /* Duplicate publish. dup flag is marked by application. * State record is not present. */ publishInfo.dup = true; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID ); + status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); } @@ -3794,7 +4184,7 @@ void test_MQTT_Publish_Send_Timeout( void ) setupNetworkBuffer( &networkBuffer ); setupTransportInterface( &transport ); - transport.writev = transportWritevFail; + transport.writev = NULL; /* Set the transport send function to the mock that always returns zero * bytes for the test. */ @@ -3811,12 +4201,13 @@ void test_MQTT_Publish_Send_Timeout( void ) memset( &publishInfo, 0, sizeof( MQTTPublishInfo_t ) ); publishInfo.pPayload = "Test"; publishInfo.payloadLength = 4; + MQTT_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPublishPacketSize_IgnoreAndReturn( MQTTSuccess ); MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess ); /* Call the API function under test and expect that it detects a timeout in sending * MQTT packet over the network. */ - status = MQTT_Publish( &mqttContext, &publishInfo, 0 ); + status = MQTT_Publish( &mqttContext, &publishInfo, 0, NULL ); TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); } @@ -3843,15 +4234,74 @@ void test_MQTT_Disconnect_already_disconnected( void ) /* Send failure with network error. */ MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetDisconnectPacketSize_ReturnThruPtr_pPacketSize( &disconnectSize ); - MQTT_SerializeDisconnect_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Disconnect( &mqttContext ); + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_Disconnect( &mqttContext, NULL, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); TEST_ASSERT_EQUAL( MQTTStatusNotConnected, status ); } +void test_MQTTV5_Disconnect() +{ + MQTTStatus_t status; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + status = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + context.connectStatus = MQTTConnected; + /*Invalid parameters*/ + status = MQTT_Disconnect( NULL, NULL, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter ); + status = MQTT_Disconnect( &context, NULL, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + /* / *Bad Parameters* / */ + MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter ); + status = MQTT_Disconnect( &context, NULL, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + MQTTPropBuilder_t propBuilder = { 0 }; + uint8_t buf[ 500 ]; + size_t bufLength = sizeof( buf ); + propBuilder.pBuffer = buf; + propBuilder.bufferLength = bufLength; + + MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb ); + status = MQTT_Disconnect( &context, &propBuilder, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Test MQTT_Disconnect with NULL property buffer.*/ + context.connectStatus = MQTTConnected; + propBuilder.pBuffer = NULL; + MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb ); + status = MQTT_Disconnect( &context, &propBuilder, MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + propBuilder.pBuffer = buf; + + /*Send failed*/ + context.connectStatus = MQTTConnected; + context.transportInterface.send = transportSendFailure; + context.transportInterface.writev = NULL; + MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb ); + status = MQTT_Disconnect( &context, NULL, 0 ); + TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); +} + /** * @brief Test that MQTT_Disconnect works as intended. */ -void test_MQTT_Disconnect1( void ) +void test_MQTT_Disconnect2( void ) { MQTTContext_t mqttContext = { 0 }; MQTTStatus_t status; @@ -3860,41 +4310,13 @@ void test_MQTT_Disconnect1( void ) NetworkContext_t networkContext = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; + size_t disconnectSize = 2; setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); networkContext.buffer = &bufPtr; transport.pNetworkContext = &networkContext; - transport.recv = transportRecvSuccess; - transport.send = transportSendFailure; - - memset( &mqttContext, 0x0, sizeof( mqttContext ) ); - MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); - mqttContext.connectStatus = MQTTConnected; - - /* Verify parameters. */ - status = MQTT_Disconnect( NULL ); - TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); -} - -/** - * @brief Test that MQTT_Disconnect works as intended. - */ -void test_MQTT_Disconnect2( void ) -{ - MQTTContext_t mqttContext = { 0 }; - MQTTStatus_t status; - uint8_t buffer[ 10 ]; - uint8_t * bufPtr = buffer; - NetworkContext_t networkContext = { 0 }; - TransportInterface_t transport = { 0 }; - MQTTFixedBuffer_t networkBuffer = { 0 }; - size_t disconnectSize = 2; - - setupTransportInterface( &transport ); - setupNetworkBuffer( &networkBuffer ); - networkContext.buffer = &bufPtr; - transport.pNetworkContext = &networkContext; + transport.writev = NULL; transport.recv = transportRecvSuccess; transport.send = transportSendFailure; @@ -3905,8 +4327,9 @@ void test_MQTT_Disconnect2( void ) /* Send failure with network error. */ MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetDisconnectPacketSize_ReturnThruPtr_pPacketSize( &disconnectSize ); - MQTT_SerializeDisconnect_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Disconnect( &mqttContext ); + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb ); + status = MQTT_Disconnect( &mqttContext, NULL, 0x00 ); TEST_ASSERT_EQUAL( MQTTSendFailed, status ); TEST_ASSERT_EQUAL( MQTTNotConnected, mqttContext.connectStatus ); } @@ -3923,7 +4346,6 @@ void test_MQTT_Disconnect3( void ) NetworkContext_t networkContext = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; - size_t disconnectSize = 2; setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -3931,31 +4353,21 @@ void test_MQTT_Disconnect3( void ) transport.pNetworkContext = &networkContext; transport.recv = transportRecvSuccess; transport.send = transportSendFailure; + transport.writev = NULL; + transport.send = transportSendNoBytes; memset( &mqttContext, 0x0, sizeof( mqttContext ) ); MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); mqttContext.connectStatus = MQTTConnected; /* Send failure with timeout in calling transport send. */ - transport.send = transportSendNoBytes; MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_GetDisconnectPacketSize_ReturnThruPtr_pPacketSize( &disconnectSize ); - MQTT_SerializeDisconnect_ExpectAnyArgsAndReturn( MQTTSuccess ); - status = MQTT_Disconnect( &mqttContext ); + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb ); + status = MQTT_Disconnect( &mqttContext, NULL, 0x00 ); TEST_ASSERT_EQUAL( MQTTSendFailed, status ); } -MQTTStatus_t MQTT_SerializeDisconnect_stub( const MQTTFixedBuffer_t * pFixedBuffer, - int numcalls ) -{ - ( void ) numcalls; - - pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT; - pFixedBuffer->pBuffer[ 1 ] = 0; - - return MQTTSuccess; -} - /** * @brief Test that MQTT_Disconnect works as intended. */ @@ -3989,12 +4401,17 @@ void test_MQTT_Disconnect4( void ) mqttContext.transportInterface.send = mockSend; MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetDisconnectPacketSize_ReturnThruPtr_pPacketSize( &disconnectSize ); - MQTT_SerializeDisconnect_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_SerializeDisconnect_Stub( MQTT_SerializeDisconnect_stub ); - /* Write a disconnect packet into the buffer. */ + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb ); /* Write a disconnect packet into the buffer. */ mqttBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT; - status = MQTT_Disconnect( &mqttContext ); + MQTTPropBuilder_t propBuffer = { 0 }; + uint8_t buf[ 10 ]; + propBuffer.pBuffer = buf; + propBuffer.bufferLength = sizeof( buf ); + propBuffer.currentIndex = 5; + + status = MQTT_Disconnect( &mqttContext, &propBuffer, 0x00 ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_EQUAL( MQTTNotConnected, mqttContext.connectStatus ); @@ -4035,19 +4452,18 @@ void test_MQTT_Disconnect4_status_disconnect_pending( void ) mqttContext.transportInterface.send = mockSend; MQTT_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetDisconnectPacketSize_ReturnThruPtr_pPacketSize( &disconnectSize ); - MQTT_SerializeDisconnect_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_SerializeDisconnect_Stub( MQTT_SerializeDisconnect_stub ); + MQTT_ValidateDisconnectProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb ); /* Write a disconnect packet into the buffer. */ mqttBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT; - status = MQTT_Disconnect( &mqttContext ); + status = MQTT_Disconnect( &mqttContext, NULL, 0 ); TEST_ASSERT_EQUAL( MQTTSuccess, status ); TEST_ASSERT_EQUAL( MQTTNotConnected, mqttContext.connectStatus ); /* At disconnect, the buffer is cleared of any pending packets. */ TEST_ASSERT_EACH_EQUAL_UINT8( 0, mqttBuffer, MQTT_TEST_BUFFER_LENGTH ); } -/* ========================================================================== */ /** * @brief Test that MQTT_GetPacketId works as intended. @@ -4251,11 +4667,8 @@ void test_MQTT_ProcessLoop_HandleKeepAlive1( void ) setupTransportInterface( &transport ); transport.recv = transportRecvNoData; setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); - context.connectStatus = MQTTConnected; - /* Verify MQTTSuccess is returned. */ MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize ); @@ -4308,7 +4721,6 @@ void test_MQTT_ProcessLoop_RecvFailed( void ) setupNetworkBuffer( &networkBuffer ); mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); - context.connectStatus = MQTTConnected; mqttStatus = MQTT_ProcessLoop( &context ); @@ -4377,9 +4789,7 @@ void test_MQTT_ProcessLoop_discardPacket_second_recv_fail( void ) mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - context.networkBuffer.size = 20; incomingPacket.type = currentPacketType; @@ -4407,16 +4817,23 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path1( void ) MQTTFixedBuffer_t networkBuffer = { 0 }; ProcessLoopReturns_t expectParams = { 0 }; MQTTPubAckInfo_t pIncomingCallback[ 10 ]; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingCallback, 10 ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingCallback, 10, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + context.connectStatus = MQTTConnected; modifyIncomingPacketStatus = MQTTSuccess; @@ -4446,16 +4863,23 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path2( void ) MQTTFixedBuffer_t networkBuffer = { 0 }; ProcessLoopReturns_t expectParams = { 0 }; MQTTPubAckInfo_t pIncomingPublish[ 10 ]; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10 ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + context.connectStatus = MQTTConnected; modifyIncomingPacketStatus = MQTTSuccess; @@ -4487,6 +4911,8 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path3( void ) ProcessLoopReturns_t expectParams = { 0 }; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -4498,9 +4924,14 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path3( void ) &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; context.connectStatus = MQTTConnected; @@ -4540,15 +4971,21 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path4( void ) MQTTFixedBuffer_t networkBuffer = { 0 }; ProcessLoopReturns_t expectParams = { 0 }; MQTTPubAckInfo_t pIncomingPublish[ 10 ]; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10 ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; context.connectStatus = MQTTConnected; @@ -4583,6 +5020,8 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path5( void ) MQTTFixedBuffer_t networkBuffer = { 0 }; ProcessLoopReturns_t expectParams = { 0 }; MQTTPubAckInfo_t pIncomingPublish[ 10 ]; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -4590,8 +5029,12 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path5( void ) mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10 ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; context.connectStatus = MQTTConnected; @@ -4623,14 +5066,21 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Path6( void ) MQTTFixedBuffer_t networkBuffer = { 0 }; ProcessLoopReturns_t expectParams = { 0 }; MQTTPubAckInfo_t pIncomingPublish[ 10 ]; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10 ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, pIncomingPublish, 10, ackPropsBuf, ackPropsBufLength ); + MQTTPropBuilder_t ackPropsBuffer; + ackPropsBuffer.pBuffer = ackPropsBuf; + ackPropsBuffer.bufferLength = ackPropsBufLength; + context.ackPropsBuffer = ackPropsBuffer; TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); context.connectStatus = MQTTConnected; @@ -4668,16 +5118,22 @@ void test_MQTT_ProcessLoop_handleIncomingPublish_Error_Paths( void ) ProcessLoopReturns_t expectParams = { 0 }; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); modifyIncomingPacketStatus = MQTTSuccess; @@ -4776,7 +5232,7 @@ void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths( void ) setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); context.connectStatus = MQTTConnected; @@ -4827,6 +5283,25 @@ void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths( void ) expectParams.stateAfterDeserialize = MQTTPublishDone; expectParams.stateAfterSerialize = MQTTPublishDone; expectProcessLoopCalls( &context, &expectParams ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths5( void ) +{ + MQTTStatus_t mqttStatus; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + ProcessLoopReturns_t expectParams = { 0 }; + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + context.connectStatus = MQTTConnected; + + modifyIncomingPacketStatus = MQTTSuccess; /* Mock the receiving of a PINGRESP packet type and expect the appropriate * calls made from the process loop. */ @@ -4849,6 +5324,7 @@ void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths( void ) /* Set expected return values in the loop. */ resetProcessLoopParams( &expectParams ); expectParams.deserializeStatus = MQTTServerRefused; + expectParams.processLoopStatus = MQTTServerRefused; expectProcessLoopCalls( &context, &expectParams ); TEST_ASSERT_TRUE( isEventCallbackInvoked ); @@ -4872,13 +5348,23 @@ void test_MQTT_ProcessLoop_handleIncomingAck_Clear_Publish_Copies( void ) TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; ProcessLoopReturns_t expectParams = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); mqttStatus = MQTT_InitRetransmits( &context, publishStoreCallbackSuccess, publishRetrieveCallbackSuccess, publishClearCallback ); @@ -4926,11 +5412,13 @@ void test_MQTT_ProcessLoop_handleIncomingAck_Clear_Publish_Copies( void ) expectProcessLoopCalls( &context, &expectParams ); } + /** * @brief This test case covers all calls to the private method, * handleIncomingAck(...), * that result in the process loop returning an error. */ + void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths( void ) { MQTTStatus_t mqttStatus; @@ -4971,6 +5459,130 @@ void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths( void ) /* Verify that MQTTStatusNotConnected propagated when receiving a any ACK, * here PUBREC but thr connection status is MQTTNotConnected. */ + context.connectStatus = MQTTNotConnected; + currentPacketType = MQTT_PACKET_TYPE_PUBREC; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.stateAfterDeserialize = MQTTPubRelSend; + expectParams.stateAfterSerialize = MQTTPubCompPending; + expectParams.serializeStatus = MQTTSuccess; + expectParams.processLoopStatus = MQTTStatusNotConnected; + context.connectStatus = MQTTNotConnected; + expectProcessLoopCalls( &context, &expectParams ); + context.connectStatus = MQTTConnected; + + /* Verify that MQTTStatusNotConnected propagated when receiving a any ACK, + * here PUBREC but thr connection status is MQTTNotConnected. */ + currentPacketType = MQTT_PACKET_TYPE_PUBREC; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.stateAfterDeserialize = MQTTPubRelSend; + expectParams.stateAfterSerialize = MQTTPubCompPending; + expectParams.serializeStatus = MQTTSuccess; + expectParams.processLoopStatus = MQTTStatusDisconnectPending; + context.connectStatus = MQTTDisconnectPending; + expectProcessLoopCalls( &context, &expectParams ); + context.connectStatus = MQTTConnected; + + /* Verify that MQTTBadResponse is propagated when deserialization fails upon + * receiving a PUBACK. */ + currentPacketType = MQTT_PACKET_TYPE_PUBACK; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.deserializeStatus = MQTTBadResponse; + expectParams.stateAfterDeserialize = MQTTStateNull; + expectParams.processLoopStatus = MQTTBadResponse; + /* The other loop parameter fields are irrelevant. */ + expectProcessLoopCalls( &context, &expectParams ); + + /* Verify that MQTTBadResponse is propagated when deserialization fails upon + * receiving a PINGRESP. */ + currentPacketType = MQTT_PACKET_TYPE_PINGRESP; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.deserializeStatus = MQTTBadResponse; + expectParams.stateAfterDeserialize = MQTTStateNull; + expectParams.processLoopStatus = MQTTBadResponse; + /* The other loop parameter fields are irrelevant. */ + expectProcessLoopCalls( &context, &expectParams ); + + /* Verify that MQTTBadResponse is propagated when deserialization fails upon + * receiving a SUBACK. */ + currentPacketType = MQTT_PACKET_TYPE_SUBACK; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.deserializeStatus = MQTTBadResponse; + expectParams.processLoopStatus = MQTTBadResponse; + /* The other loop parameter fields are irrelevant. */ + expectProcessLoopCalls( &context, &expectParams ); + + /* Verify that MQTTIllegalState is returned if MQTT_UpdateStateAck(...) + * provides an unknown state such as MQTTStateNull to sendPublishAcks(...). */ + currentPacketType = MQTT_PACKET_TYPE_PUBREC; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.stateAfterDeserialize = MQTTPubRelSend; + expectParams.stateAfterSerialize = MQTTStateNull; + expectParams.processLoopStatus = MQTTIllegalState; + expectProcessLoopCalls( &context, &expectParams ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Error_PathsWithProperties( void ) +{ + MQTTStatus_t mqttStatus; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + ProcessLoopReturns_t expectParams = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + context.connectStatus = MQTTConnected; + + modifyIncomingPacketStatus = MQTTSuccess; + + /* Verify that MQTTBadResponse is propagated when deserialization fails upon + * receiving an unknown packet type. */ + currentPacketType = MQTT_PACKET_TYPE_INVALID; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.deserializeStatus = MQTTBadResponse; + expectParams.processLoopStatus = MQTTBadResponse; + expectProcessLoopCalls( &context, &expectParams ); + + /* Verify that MQTTStatusNotConnected propagated when receiving a any ACK, + * here PUBREC but thr connection status is MQTTNotConnected. */ + + /* Verify that MQTTStatusNotConnected propagated when receiving a any ACK, + * here PUBREC but thr connection status is MQTTNotConnected. */ + context.connectStatus = MQTTNotConnected; + currentPacketType = MQTT_PACKET_TYPE_PUBREC; + /* Set expected return values in the loop. */ + resetProcessLoopParams( &expectParams ); + expectParams.stateAfterDeserialize = MQTTPubRelSend; + expectParams.stateAfterSerialize = MQTTPubCompPending; + expectParams.serializeStatus = MQTTSuccess; + expectParams.processLoopStatus = MQTTStatusNotConnected; + context.connectStatus = MQTTNotConnected; + expectProcessLoopCalls( &context, &expectParams ); + + + context.connectStatus = MQTTNotConnected; currentPacketType = MQTT_PACKET_TYPE_PUBREC; /* Set expected return values in the loop. */ resetProcessLoopParams( &expectParams ); @@ -5038,6 +5650,61 @@ void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths( void ) expectProcessLoopCalls( &context, &expectParams ); } +void test_MQTT_ProcessLoop_handleIncomingAck_InvalidReasonCode( void ) +{ + MQTTStatus_t mqttStatus; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + ProcessLoopReturns_t expectParams = { 0 }; + + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallbackInvalidRC, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + + context.connectStatus = MQTTConnected; + + context.waitingForPingResp = false; + context.keepAliveIntervalSec = 0; + expectParams.incomingPublish = false; + expectParams.updateStateStatus = MQTTSuccess; + expectParams.processLoopStatus = MQTTSuccess; + expectParams.stateAfterDeserialize = MQTTPubRelSend; + /* Set expected return values in the loop. All success. */ + MQTTPacketInfo_t incomingPacket = { 0 }; + /* Modify incoming packet depending on type to be tested. */ + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + + + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( expectParams.updateStateStatus ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &expectParams.stateAfterDeserialize ); + + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); +} + /** * @brief This test case covers all calls to the private method, * handleKeepAlive(...), @@ -5073,7 +5740,7 @@ void test_MQTT_ProcessLoop_handleKeepAlive_Happy_Paths1( void ) /* Set expected return values in the loop. All success. */ MQTTPacketInfo_t incomingPacket = { 0 }; /* Modify incoming packet depending on type to be tested. */ - incomingPacket.type = currentPacketType; + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; @@ -5128,7 +5795,7 @@ void test_MQTT_ProcessLoop_handleKeepAlive_Happy_Paths2( void ) MQTTPacketInfo_t incomingPacket = { 0 }; /* Modify incoming packet depending on type to be tested. */ - incomingPacket.type = currentPacketType; + incomingPacket.type = MQTT_PACKET_TYPE_PUBACK; incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; @@ -5177,7 +5844,7 @@ void test_MQTT_ProcessLoop_handleKeepAlive_Happy_Paths3( void ) MQTTPacketInfo_t incomingPacket = { 0 }; /* Modify incoming packet depending on type to be tested. */ - incomingPacket.type = currentPacketType; + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; @@ -5223,7 +5890,7 @@ void test_MQTT_ProcessLoop_handleKeepAlive_Happy_Paths4( void ) MQTTPacketInfo_t incomingPacket = { 0 }; /* Modify incoming packet depending on type to be tested. */ - incomingPacket.type = currentPacketType; + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; @@ -5359,8 +6026,9 @@ void test_MQTT_ProcessLoop_handleKeepAlive_Error_Paths3( void ) context.waitingForPingResp = false; /* Set expected return values in the loop. */ resetProcessLoopParams( &expectParams ); - + size_t packetSize = MQTT_PACKET_PINGREQ_SIZE; MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_SerializePingreq_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttStatus = MQTT_ProcessLoop( &context ); @@ -5379,6 +6047,7 @@ void test_MQTT_ProcessLoop_handleKeepAlive_Error_Paths4( void ) TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; ProcessLoopReturns_t expectParams = { 0 }; + size_t packetSize = MQTT_PACKET_PINGREQ_SIZE; setupTransportInterface( &transport ); transport.recv = transportRecvNoData; @@ -5419,6 +6088,7 @@ void test_MQTT_ProcessLoop_handleKeepAlive_Error_Paths4( void ) MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_SerializePingreq_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttStatus = MQTT_ProcessLoop( &context ); @@ -5523,6 +6193,8 @@ void test_MQTT_ProcessLoop_Timer_Overflow( void ) MQTTPublishState_t publishState = MQTTPubAckSend; MQTTPublishState_t ackState = MQTTPublishDone; MQTTPubAckInfo_t incomingPublishRecords[ 10 ]; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -5535,7 +6207,12 @@ void test_MQTT_ProcessLoop_Timer_Overflow( void ) mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, incomingPublishRecords, 10 ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + + mqttStatus = MQTT_InitStatefulQoS( &context, NULL, 0, incomingPublishRecords, 10, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); context.connectStatus = MQTTConnected; @@ -5569,14 +6246,27 @@ void test_MQTT_ReceiveLoop( void ) TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - /* Verify that a NULL Context returns an error. */ - mqttStatus = MQTT_ReceiveLoop( NULL ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + /* Verify that a NULL Context returns an error. */ + mqttStatus = MQTT_ReceiveLoop( NULL ); TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); /* Verify that a NULL time function returns an error. */ @@ -5623,65 +6313,503 @@ void test_MQTT_ReceiveLoop( void ) TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); } -/* ========================================================================== */ -/** - * @brief This test case verifies that MQTT_Subscribe returns MQTTBadParameter - * with an invalid parameter. This test case also gives us coverage over - * the private method, validateSubscribeUnsubscribeParams(...). - */ -void test_MQTT_Subscribe_invalid_params( void ) + +void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths1( void ) { - MQTTStatus_t mqttStatus; + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTPublishState_t stateAfterSerialize; MQTTContext_t context = { 0 }; - MQTTSubscribeInfo_t subscribeInfo = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); - /* Call subscribe with a NULL context. */ - mqttStatus = MQTT_Subscribe( NULL, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + /*Update state returns bad response.*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + status = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); - /* Call subscribe with a NULL subscription list. */ - mqttStatus = MQTT_Subscribe( &context, NULL, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); - /* Call subscribe with 0 subscriptions. */ - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 0, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + context.connectStatus = MQTTConnected; - /* Packet ID cannot be 0 per MQTT 3.1.1 spec. */ - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, 0 ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); - /* Incoming publish records NULL but QoS > 0. */ - subscribeInfo.qos = MQTTQoS1; - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, 10 ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + stateAfterSerialize = MQTTPubCompPending; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTBadResponse ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); } -static uint8_t * MQTT_SerializeSubscribedHeader_cb( size_t remainingLength, - uint8_t * pIndex, - uint16_t packetId, - int numcallbacks ) +void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths2( void ) { - ( void ) remainingLength; - ( void ) pIndex; - ( void ) packetId; - ( void ) numcallbacks; + MQTTStatus_t status; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTPacketInfo_t incomingPacket = { 0 }; - return pIndex; + /*Update state returns bad response.*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + status = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + context.connectStatus = MQTTConnected; + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + + modifyIncomingPacketStatus = MQTTSuccess; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTBadParameter ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths3( void ) +{ + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + uint16_t packetId = 2; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + + /*Invalid packet parameters.*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + status = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + context.connectStatus = MQTTConnected; + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_DeserializeAck_ReturnThruPtr_pPacketId( &packetId ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths4( void ) +{ + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + uint16_t packetId = 2; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + + /*Invalid packet parameters.*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + status = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + context.connectStatus = MQTTConnected; + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_DeserializeAck_ReturnThruPtr_pPacketId( &packetId ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTBadParameter ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths2( void ) +{ + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTPublishState_t stateAfterSerialize; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + uint16_t packetId = 1; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + /*Using event call back to set reason string and user properties,*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + status = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + context.connectStatus = MQTTConnected; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + stateAfterSerialize = MQTTPubCompPending; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_DeserializeAck_ReturnThruPtr_pPacketId( &packetId ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingPublish_Happy_Paths3( void ) +{ + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTPublishState_t stateAfterSerialize; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + uint16_t packetId = 1; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + /*Using event call back to set reason string and user properties,*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBLISH; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + status = MQTT_Init( &context, &transport, getTime, eventCallback3, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + context.connectStatus = MQTTConnected; + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + stateAfterSerialize = MQTTPubCompPending; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializePublish_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_DeserializePublish_ReturnThruPtr_pPacketId( &packetId ); + MQTT_UpdateStatePublish_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStatePublish_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths3( void ) +{ + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTPublishState_t stateAfterSerialize; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + uint16_t packetId = 1; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + /*Using event call back to set reason string and user properties,*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + status = MQTT_Init( &context, &transport, getTime, eventCallback3, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + context.connectStatus = MQTTConnected; + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + stateAfterSerialize = MQTTPubCompPending; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_DeserializeAck_ReturnThruPtr_pPacketId( &packetId ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths4( void ) +{ + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTPublishState_t stateAfterSerialize; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + uint16_t packetId = 1; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + /*Using event call back to set reason string and user properties,*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + status = MQTT_Init( &context, &transport, getTime, eventCallback4, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + context.connectStatus = MQTTConnected; + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + stateAfterSerialize = MQTTPubCompPending; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_DeserializeAck_ReturnThruPtr_pPacketId( &packetId ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths5( void ) +{ + MQTTStatus_t status; + MQTTPublishState_t stateAfterDeserialize; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint16_t packetId = 1; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + /*Unable to send the packet using transport interface.*/ + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + incomingPacket.type = MQTT_PACKET_TYPE_PUBREC; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + status = MQTT_Init( &context, &transport, getTime, eventCallback2, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + context.connectStatus = MQTTConnected; + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + context.transportInterface.send = transportSendNoBytes; + context.transportInterface.writev = NULL; + modifyIncomingPacketStatus = MQTTSuccess; + stateAfterDeserialize = MQTTPubRelSend; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_DeserializeAck_ReturnThruPtr_pPacketId( &packetId ); + MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize ); + MQTT_ValidatePublishAckProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status ); +} + +void test_MQTT_ProcessLoop_handleIncomingDisconnect( void ) +{ + MQTTStatus_t status; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTReasonCodeInfo_t disconnectInfo; + MQTTPacketInfo_t incomingPacket = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + + memset( &disconnectInfo, 0x0, sizeof( disconnectInfo ) ); + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + status = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + incomingPacket.type = MQTT_PACKET_TYPE_DISCONNECT; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeDisconnect_IgnoreAndReturn( MQTTSuccess ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); + + /*Invalid packet parameters.*/ + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeDisconnect_IgnoreAndReturn( MQTTBadResponse ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status ); } -static uint8_t * MQTT_SerializeSubscribedHeader_cb1( size_t remainingLength, - uint8_t * pIndex, - uint16_t packetId, - int numcallbacks ) +static void setupSubscriptionInfo( MQTTSubscribeInfo_t * pSubscribeInfo ) +{ + pSubscribeInfo->qos = MQTTQoS1; + pSubscribeInfo->pTopicFilter = MQTT_SAMPLE_TOPIC_FILTER; + pSubscribeInfo->topicFilterLength = MQTT_SAMPLE_TOPIC_FILTER_LENGTH; + pSubscribeInfo->noLocalOption = 0; + pSubscribeInfo->retainAsPublishedOption = 0; + pSubscribeInfo->retainHandlingOption = retainSendOnSubIfNotPresent; +} + + +static uint8_t * MQTTV5_SerializeSubscribedHeader_cb( size_t remainingLength, + uint8_t * pIndex, + uint16_t packetId, + int numcallbacks ) { ( void ) remainingLength; ( void ) pIndex; ( void ) packetId; ( void ) numcallbacks; - return pIndex + 5; + return pIndex; } static uint8_t * MQTT_SerializeSubscribedHeader_cb2( size_t remainingLength, @@ -5714,17 +6842,177 @@ static uint8_t * MQTT_SerializeUnsubscribedHeader_cb2( size_t remainingLength, return pIndex + UnsubscribeHeaderLength; } -/** - * @brief This test case verifies that MQTT_Subscribe returns successfully - * when valid parameters are passed and all bytes are sent. - */ -void test_MQTT_Subscribe_happy_path( void ) + +void test_MQTTV5_Subscribe_invalid_params( void ) +{ + MQTTStatus_t mqttStatus; + MQTTContext_t context = { 0 }; + MQTTSubscribeInfo_t subscribeInfo = { 0 }; + + /* Call subscribe with a NULL context. */ + mqttStatus = MQTT_Subscribe( NULL, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + + /* Call with a NULL subscription list. */ + mqttStatus = MQTT_Subscribe( &context, NULL, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Call subscribe with 0 subscriptions. */ + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 0, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Packet ID cannot be 0 per MQTT 5.0 spec. */ + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, 0, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Incoming publish records NULL but QoS > 0. */ + subscribeInfo.qos = MQTTQoS1; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, 10, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + subscribeInfo.qos = MQTTQoS0; + + /* Test topic filter length is zero */ + subscribeInfo.pTopicFilter = "test/topic"; + subscribeInfo.topicFilterLength = 0; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test NULL topic filter */ + subscribeInfo.pTopicFilter = NULL; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.topicFilterLength = 2; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.pTopicFilter = "abc/def"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + + /* Test invalid QoS level */ + subscribeInfo.qos = 3; /* QoS must be 0, 1, or 2 */ + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test invalid shared subscription */ + subscribeInfo.pTopicFilter = "$share/invalid#"; + subscribeInfo.noLocalOption = 0; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.pTopicFilter = "$share/abc/#"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.pTopicFilter = "abc"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + subscribeInfo.retainHandlingOption = 3; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.retainHandlingOption = 1; + subscribeInfo.qos = 3; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.qos = 0; + subscribeInfo.retainHandlingOption = 0; + context.connectProperties.isWildcardAvailable = 0U; + subscribeInfo.pTopicFilter = "$share/abc/#"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.qos = 0; + subscribeInfo.retainHandlingOption = 0; + context.connectProperties.isWildcardAvailable = 1U; + subscribeInfo.pTopicFilter = "$share/abc/#"; + context.connectProperties.isSharedAvailable = 0U; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.pTopicFilter = "$share/+/abc"; + context.connectProperties.isSharedAvailable = 1U; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test shared subscription with '#' in share name */ + subscribeInfo.pTopicFilter = "$share/group#name/topic"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + context.connectProperties.isSharedAvailable = 1U; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test shared subscription with empty share name */ + subscribeInfo.pTopicFilter = "$share//topic"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test shared subscription with no topic after share name */ + subscribeInfo.pTopicFilter = "$share/group/"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test topic filter with wildcard when wildcards are not supported */ + context.connectProperties.isWildcardAvailable = 0U; + subscribeInfo.pTopicFilter = "topic/#"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test topic filter with plus wildcard when wildcards are not supported */ + + context.connectProperties.isWildcardAvailable = 0U; + subscribeInfo.pTopicFilter = "topic/+/abc"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.pTopicFilter = "topic/+/subtopic/#"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + /* Test shared subscription with invalid characters in share name */ + subscribeInfo.pTopicFilter = "$share/group#/topic"; + subscribeInfo.topicFilterLength = strlen( subscribeInfo.pTopicFilter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); +} + +void test_MQTTV5_Subscribe_ValidateFailure( void ) +{ + MQTTStatus_t mqttStatus; + MQTTSubscribeInfo_t subscribeInfo; + MQTTContext_t testContext = { 0 }; + + memset( &subscribeInfo, 0, sizeof( subscribeInfo ) ); + + /* Force the validation function to return an error */ + validateSubscribeReturn = MQTTBadParameter; + + mqttStatus = MQTT_Subscribe( &testContext, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + + /* Restore for later tests */ + validateSubscribeReturn = MQTTSuccess; +} + +void test_MQTTV5_Subscribe_happy_path( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; MQTTSubscribeInfo_t subscribeInfo = { 0 }; + size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; MQTTPubAckInfo_t incomingRecords = { 0 }; @@ -5734,27 +7022,52 @@ void test_MQTT_Subscribe_happy_path( void ) setupNetworkBuffer( &networkBuffer ); setupSubscriptionInfo( &subscribeInfo ); + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttStatus = MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; + MQTTPropBuilder_t propBuilder = { 0 }; + uint8_t buf[ 500 ]; + size_t bufLength = sizeof( buf ); + propBuilder.pBuffer = buf; + propBuilder.bufferLength = bufLength; + + propBuilder.pBuffer[ 0 ] = 0x0B; + propBuilder.pBuffer[ 1 ] = 2; + propBuilder.currentIndex = 2; + /* Verify MQTTSuccess is returned with the following mocks. */ + MQTT_ValidateSubscribeProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, &propBuilder ); + + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + context.connectProperties.isWildcardAvailable = 0U; + MQTT_ValidateSubscribeProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, &propBuilder ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + MQTT_ValidateSubscribeProperties_ExpectAnyArgsAndReturn( MQTTBadParameter ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, &propBuilder ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); } /** @@ -5772,6 +7085,8 @@ void test_MQTT_Subscribe_happy_path_not_connected( void ) size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); @@ -5780,10 +7095,10 @@ void test_MQTT_Subscribe_happy_path_not_connected( void ) /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttStatus = MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); /* Test 1 connect status is MQTTNotConnected */ @@ -5792,9 +7107,9 @@ void test_MQTT_Subscribe_happy_path_not_connected( void ) MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTStatusNotConnected, mqttStatus ); /* Test 2 connect status is MQTTDisconnectPending*/ @@ -5803,17 +7118,13 @@ void test_MQTT_Subscribe_happy_path_not_connected( void ) MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTStatusDisconnectPending, mqttStatus ); } -/** - * @brief This test case verifies that MQTT_Subscribe returns successfully - * when valid parameters are passed and all bytes are sent. - */ -void test_MQTT_Subscribe_happy_path1( void ) +void test_MQTTV5_Subscribe_happy_path1( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; @@ -5825,77 +7136,116 @@ void test_MQTT_Subscribe_happy_path1( void ) MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - setupSubscriptionInfo( &subscribeInfo[ 0 ] ); - /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttStatus = MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - /* Verify MQTTSuccess is returned with the following mocks. */ + subscribeInfo[ 0 ].qos = MQTTQoS2; + subscribeInfo[ 0 ].pTopicFilter = "abc"; + subscribeInfo[ 0 ].topicFilterLength = 3; + subscribeInfo[ 0 ].noLocalOption = 1; + subscribeInfo[ 0 ].retainAsPublishedOption = 1; + subscribeInfo[ 0 ].retainHandlingOption = 0; + + subscribeInfo[ 1 ].qos = MQTTQoS0; + subscribeInfo[ 1 ].pTopicFilter = "def"; + subscribeInfo[ 1 ].retainHandlingOption = 2; + subscribeInfo[ 1 ].topicFilterLength = 3; + subscribeInfo[ 1 ].noLocalOption = 0; + subscribeInfo[ 1 ].retainAsPublishedOption = 0; + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Subscribe( &context, subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + mqttStatus = MQTT_Subscribe( &context, subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + subscribeInfo[ 0 ].qos = MQTTQoS0; + subscribeInfo[ 0 ].pTopicFilter = "def"; + subscribeInfo[ 0 ].retainHandlingOption = 2; + subscribeInfo[ 0 ].topicFilterLength = 3; + subscribeInfo[ 0 ].noLocalOption = 0; + subscribeInfo[ 0 ].retainAsPublishedOption = 0; + + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + + mqttStatus = MQTT_Subscribe( &context, subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + subscribeInfo[ 0 ].qos = 3; + subscribeInfo[ 0 ].pTopicFilter = "def"; + subscribeInfo[ 0 ].retainHandlingOption = 2; + subscribeInfo[ 0 ].topicFilterLength = 3; + subscribeInfo[ 0 ].noLocalOption = 0; + subscribeInfo[ 0 ].retainAsPublishedOption = 0; + + mqttStatus = MQTT_Subscribe( &context, subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); } -/** - * @brief This test case verifies that MQTT_Subscribe returns successfully - * when valid parameters are passed and all bytes are sent. - */ -void test_MQTT_Subscribe_happy_path2( void ) +void test_MQTTV5_Subscribe_happy_path2( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; - MQTTSubscribeInfo_t subscribeInfo[ 2 ]; + MQTTSubscribeInfo_t subscribeInfo = { 0 }; size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - setupSubscriptionInfo( &subscribeInfo[ 0 ] ); - setupSubscriptionInfo( &subscribeInfo[ 1 ] ); - /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); mqttStatus = MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - /* Verify MQTTSuccess is returned with the following mocks. */ MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb1 ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); - /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Subscribe( &context, subscribeInfo, 2, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); -} + subscribeInfo.qos = MQTTQoS1; + subscribeInfo.pTopicFilter = "$share/abc/bcd"; + subscribeInfo.topicFilterLength = 14; + subscribeInfo.noLocalOption = 0; + subscribeInfo.retainAsPublishedOption = 1; + subscribeInfo.retainHandlingOption = 0; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); +} void test_MQTT_Subscribe_MultipleSubscriptions( void ) { MQTTStatus_t mqttStatus; @@ -5919,18 +7269,30 @@ void test_MQTT_Subscribe_MultipleSubscriptions( void ) subscribeInfo[ 0 ].qos = MQTTQoS1; subscribeInfo[ 0 ].pTopicFilter = MQTT_SAMPLE_TOPIC_FILTER; subscribeInfo[ 0 ].topicFilterLength = MQTT_SAMPLE_TOPIC_FILTER_LENGTH; + subscribeInfo[ 0 ].noLocalOption = 0; + subscribeInfo[ 0 ].retainAsPublishedOption = 0; + subscribeInfo[ 0 ].retainHandlingOption = retainSendOnSub; subscribeInfo[ 1 ].qos = MQTTQoS2; subscribeInfo[ 1 ].pTopicFilter = MQTT_SAMPLE_TOPIC_FILTER1; subscribeInfo[ 1 ].topicFilterLength = MQTT_SAMPLE_TOPIC_FILTER_LENGTH1; + subscribeInfo[ 1 ].noLocalOption = 0; + subscribeInfo[ 1 ].retainAsPublishedOption = 0; + subscribeInfo[ 1 ].retainHandlingOption = retainSendOnSub; subscribeInfo[ 2 ].qos = MQTTQoS0; subscribeInfo[ 2 ].pTopicFilter = MQTT_SAMPLE_TOPIC_FILTER2; subscribeInfo[ 2 ].topicFilterLength = MQTT_SAMPLE_TOPIC_FILTER_LENGTH2; + subscribeInfo[ 2 ].noLocalOption = 0; + subscribeInfo[ 2 ].retainAsPublishedOption = 0; + subscribeInfo[ 2 ].retainHandlingOption = retainSendOnSub; subscribeInfo[ 3 ].qos = MQTTQoS1; subscribeInfo[ 3 ].pTopicFilter = MQTT_SAMPLE_TOPIC_FILTER3; subscribeInfo[ 3 ].topicFilterLength = MQTT_SAMPLE_TOPIC_FILTER_LENGTH3; + subscribeInfo[ 3 ].noLocalOption = 0; + subscribeInfo[ 3 ].retainAsPublishedOption = 0; + subscribeInfo[ 3 ].retainHandlingOption = retainSendOnSub; /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); @@ -5938,7 +7300,7 @@ void test_MQTT_Subscribe_MultipleSubscriptions( void ) mqttStatus = MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, NULL, 0 ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); context.connectStatus = MQTTConnected; @@ -5950,56 +7312,65 @@ void test_MQTT_Subscribe_MultipleSubscriptions( void ) MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb2 ); /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Subscribe( &context, subscribeInfo, 4, MQTT_FIRST_VALID_PACKET_ID ); + mqttStatus = MQTT_Subscribe( &context, subscribeInfo, 4, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); } -/** - * @brief This test case verifies that MQTT_Subscribe returns MQTTSendFailed - * if transport interface send returns an error. - */ -void test_MQTT_Subscribe_error_paths1( void ) +void test_MQTTV5_Subscribe_happy_path3( void ) { - MQTTStatus_t mqttStatus = { 0 }; + MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; MQTTSubscribeInfo_t subscribeInfo = { 0 }; size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); - /* Verify that an error is propagated when transport interface returns an error. */ - setupNetworkBuffer( &networkBuffer ); - setupSubscriptionInfo( &subscribeInfo ); - subscribeInfo.qos = MQTTQoS0; setupTransportInterface( &transport ); - transport.send = transportSendFailure; - transport.writev = transportWritevFail; + setupNetworkBuffer( &networkBuffer ); - /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); context.connectStatus = MQTTConnected; - - /* Verify MQTTSendFailed is propagated when transport interface returns an error. */ + /* Verify MQTTSuccess is returned with the following mocks. */ MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); - /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + + subscribeInfo.qos = MQTTQoS1; + subscribeInfo.pTopicFilter = "$share/abc/def"; + subscribeInfo.topicFilterLength = 11; + subscribeInfo.noLocalOption = 0; + subscribeInfo.retainAsPublishedOption = 1; + subscribeInfo.retainHandlingOption = 0; + + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); } /** * @brief This test case verifies that MQTT_Subscribe returns MQTTSendFailed * if transport interface send returns an error. */ -void test_MQTT_Subscribe_error_paths2( void ) +void test_MQTT_Subscribe_error_paths1( void ) { - MQTTStatus_t mqttStatus; + MQTTStatus_t mqttStatus = { 0 }; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; @@ -6012,9 +7383,8 @@ void test_MQTT_Subscribe_error_paths2( void ) setupSubscriptionInfo( &subscribeInfo ); subscribeInfo.qos = MQTTQoS0; setupTransportInterface( &transport ); + transport.send = transportSendFailure; transport.writev = NULL; - /* Case when there is timeout in sending data through transport send. */ - transport.send = transportSendNoBytes; /* Use the mock function that returns zero bytes sent. */ /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); @@ -6022,22 +7392,23 @@ void test_MQTT_Subscribe_error_paths2( void ) context.connectStatus = MQTTConnected; + /* Verify MQTTSendFailed is propagated when transport interface returns an error. */ MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + /* Expect the above calls when running MQTT_Subscribe. */ + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); } /** - * @brief This test case verifies that MQTT_Subscribe returns MQTTSendFailed - * if transport interface fails to send and the connection status is converted to - * MQTTDisconnectPending + * @brief This test case verifies that MQTT_Unsubscribe returns MQTTSendFailed + * if transport interface send returns an error. */ -void test_MQTT_Subscribe_error_paths_with_transport_failure( void ) +void test_MQTT_Unsubscribe_error_paths1( void ) { - MQTTStatus_t mqttStatus; + MQTTStatus_t mqttStatus = { 0 }; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; @@ -6050,8 +7421,8 @@ void test_MQTT_Subscribe_error_paths_with_transport_failure( void ) setupSubscriptionInfo( &subscribeInfo ); subscribeInfo.qos = MQTTQoS0; setupTransportInterface( &transport ); - transport.writev = NULL; transport.send = transportSendFailure; + transport.writev = NULL; /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); @@ -6059,13 +7430,14 @@ void test_MQTT_Subscribe_error_paths_with_transport_failure( void ) context.connectStatus = MQTTConnected; - MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); - MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + /* Verify MQTTSendFailed is propagated when transport interface returns an error. */ + MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeUnsubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + /* Expect the above calls when running MQTT_Subscribe. */ + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); - TEST_ASSERT_EQUAL( MQTTDisconnectPending, context.connectStatus ); } /** @@ -6107,8 +7479,8 @@ void test_MQTT_Subscribe_error_paths_timerOverflowCheck( void ) MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); TEST_ASSERT_EQUAL( -1, getTimeMockCallLimit ); } @@ -6152,60 +7524,55 @@ void test_MQTT_Subscribe_error_paths_timerOverflowCheck1( void ) MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeSubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); - mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); TEST_ASSERT_EQUAL( -1, getTimeMockBigTimeStepCallLimit ); } -/* ========================================================================== */ - /** - * @brief This test case verifies that MQTT_Unsubscribe returns MQTTBadParameter - * with an invalid parameter. This test case also gives us coverage over - * the private method, validateSubscribeUnsubscribeParams(...). + * @brief This test case verifies that MQTT_Subscribe returns MQTTSendFailed + * if transport interface send returns an error. */ -void test_MQTT_Unsubscribe_invalid_params( void ) +void test_MQTT_Subscribe_error_paths2( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; MQTTSubscribeInfo_t subscribeInfo = { 0 }; + size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; - /* Call subscribe with a NULL context. */ - mqttStatus = MQTT_Unsubscribe( NULL, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); - - /* Call subscribe with a NULL subscription list. */ - mqttStatus = MQTT_Unsubscribe( &context, NULL, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); - - /* Call subscribe with 0 subscriptions. */ - mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 0, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + /* Verify that an error is propagated when transport interface returns an error. */ + setupNetworkBuffer( &networkBuffer ); + setupSubscriptionInfo( &subscribeInfo ); + subscribeInfo.qos = MQTTQoS0; + setupTransportInterface( &transport ); + transport.writev = NULL; + /* Case when there is timeout in sending data through transport send. */ + transport.send = transportSendNoBytes; /* Use the mock function that returns zero bytes sent. */ - /* Packet ID cannot be 0 per MQTT 3.1.1 spec. */ - mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, 0 ); - TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); -} + /* Initialize context. */ + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); -static uint8_t * MQTT_SerializeUnsubscribeHeader_cb( size_t remainingLength, - uint8_t * pIndex, - uint16_t packetId, - int numcallbacks ) -{ - ( void ) remainingLength; - ( void ) pIndex; - ( void ) packetId; - ( void ) numcallbacks; + context.connectStatus = MQTTConnected; - return pIndex; + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); } /** - * @brief This test case verifies that MQTT_Unsubscribe returns successfully - * when valid parameters are passed and all bytes are sent. + * @brief This test case verifies that MQTT_Subscribe returns MQTTSendFailed + * if transport interface fails to send and the connection status is converted to + * MQTTDisconnectPending */ -void test_MQTT_Unsubscribe_happy_path( void ) +void test_MQTT_Subscribe_error_paths_with_transport_failure( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; @@ -6215,10 +7582,13 @@ void test_MQTT_Unsubscribe_happy_path( void ) size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; - setupTransportInterface( &transport ); + /* Verify that an error is propagated when transport interface returns an error. */ setupNetworkBuffer( &networkBuffer ); setupSubscriptionInfo( &subscribeInfo ); subscribeInfo.qos = MQTTQoS0; + setupTransportInterface( &transport ); + transport.writev = NULL; + transport.send = transportSendFailure; /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); @@ -6226,149 +7596,225 @@ void test_MQTT_Unsubscribe_happy_path( void ) context.connectStatus = MQTTConnected; - /* Verify MQTTSuccess is returned with the following mocks. */ - MQTT_SerializeUnsubscribeHeader_Stub( MQTT_SerializeUnsubscribeHeader_cb ); - MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - - /* Expect the above calls when running MQTT_Unsubscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); + TEST_ASSERT_EQUAL( MQTTDisconnectPending, context.connectStatus ); } -/** - * @brief This test case verifies that MQTT_Unsubscribe does not return success - * when the connection status is anything but MQTTConnected - */ -void test_MQTT_Unsubscribe_not_connected( void ) +void test_MQTTV5_shared_subscriptions( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; MQTTSubscribeInfo_t subscribeInfo = { 0 }; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - setupSubscriptionInfo( &subscribeInfo ); - subscribeInfo.qos = MQTTQoS0; - /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + context.connectStatus = MQTTConnected; + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); - /* Test 1 Connection status is MQTTNotConnected*/ - context.connectStatus = MQTTNotConnected; - /* Verify MQTTSuccess is returned with the following mocks. */ - MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - /* Expect the above calls when running MQTT_Unsubscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTStatusNotConnected, mqttStatus ); + subscribeInfo.pTopicFilter = "$share/abc/bcd"; + subscribeInfo.topicFilterLength = 10; + subscribeInfo.noLocalOption = 0; + subscribeInfo.retainAsPublishedOption = 1; + subscribeInfo.retainHandlingOption = 0; + subscribeInfo.qos = MQTTQoS2; - /* Test 2 Connection status is MQTTDisconnectPending*/ - context.connectStatus = MQTTDisconnectPending; - /* Verify MQTTSuccess is returned with the following mocks. */ - MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - /* Expect the above calls when running MQTT_Unsubscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTStatusDisconnectPending, mqttStatus ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, mqttStatus ); + + subscribeInfo.qos = MQTTQoS1; + subscribeInfo.retainHandlingOption = 3; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + subscribeInfo.retainHandlingOption = 0; + + + + /** Invalid Sharename */ + subscribeInfo.pTopicFilter = "$share/abc"; + subscribeInfo.topicFilterLength = 10; + + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); + + subscribeInfo.pTopicFilter = "$share/abc/bcd"; + subscribeInfo.topicFilterLength = 14; + subscribeInfo.noLocalOption = 1; + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, mqttStatus ); } -/** - * @brief This test case verifies that MQTT_Subscribe returns successfully - * when valid parameters are passed and all bytes are sent. - */ -void test_MQTT_Unsubscribe_happy_path1( void ) +/* Suback - Unsuback Happy path */ +void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths_suback( void ) { - MQTTStatus_t mqttStatus; + MQTTStatus_t status; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; - MQTTSubscribeInfo_t subscribeInfo[ 2 ]; - size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; - size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; + MQTTPacketInfo_t incomingPacket = { 0 }; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - setupSubscriptionInfo( &subscribeInfo[ 0 ] ); + /* Modify incoming packet depending on type to be tested. */ + incomingPacket.type = MQTT_PACKET_TYPE_SUBACK; + incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH; - /* Initialize context. */ - mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + status = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + + status = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); + modifyIncomingPacketStatus = MQTTSuccess; + MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket ); + MQTT_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess ); + status = MQTT_ProcessLoop( &context ); + TEST_ASSERT_EQUAL_INT( MQTTSuccess, status ); +} - mqttStatus = MQTT_InitStatefulQoS( &context, - &outgoingRecords, 4, - &incomingRecords, 4 ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); +/* ========================================================================== */ - context.connectStatus = MQTTConnected; +/** + * @brief This test case verifies that MQTT_Unsubscribe returns MQTTBadParameter + * with an invalid parameter. This test case also gives us coverage over + * the private method, validateSubscribeUnsubscribeParams(...). + */ +void test_MQTT_Unsubscribe_invalid_params( void ) +{ + MQTTStatus_t mqttStatus; + MQTTContext_t context = { 0 }; + MQTTSubscribeInfo_t subscribeInfo = { 0 }; - /* Verify MQTTSuccess is returned with the following mocks. */ - MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); - MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeUnsubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb ); + /* Call subscribe with a NULL context. */ + mqttStatus = MQTT_Unsubscribe( NULL, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); - /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); + /* Call subscribe with a NULL subscription list. */ + mqttStatus = MQTT_Unsubscribe( &context, NULL, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + /* Call subscribe with 0 subscriptions. */ + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 0, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + + /* Packet ID cannot be 0 per MQTT 3.1.1 spec. */ + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, 0, NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); +} + + +static uint8_t * MQTTV5_SerializeUnsubscribeHeader_cb( size_t remainingLength, + uint8_t * pIndex, + uint16_t packetId, + int numcallbacks ) +{ + ( void ) remainingLength; + ( void ) pIndex; + ( void ) packetId; + ( void ) numcallbacks; + + return pIndex; } /** - * @brief This test case verifies that MQTT_Subscribe returns successfully + * @brief This test case verifies that MQTT_Unsubscribe returns successfully * when valid parameters are passed and all bytes are sent. */ -void test_MQTT_unsubscribe_happy_path2( void ) +void test_MQTT_Unsubscribe_happy_path( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; TransportInterface_t transport = { 0 }; MQTTFixedBuffer_t networkBuffer = { 0 }; - MQTTSubscribeInfo_t subscribeInfo[ 3 ]; + MQTTSubscribeInfo_t subscribeInfo = { 0 }; size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; - MQTTPubAckInfo_t incomingRecords = { 0 }; - MQTTPubAckInfo_t outgoingRecords = { 0 }; setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); - setupSubscriptionInfo( &subscribeInfo[ 0 ] ); - setupSubscriptionInfo( &subscribeInfo[ 1 ] ); - setupSubscriptionInfo( &subscribeInfo[ 2 ] ); + setupSubscriptionInfo( &subscribeInfo ); + subscribeInfo.qos = MQTTQoS0; /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - mqttStatus = MQTT_InitStatefulQoS( &context, - &outgoingRecords, 4, - &incomingRecords, 4 ); - TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - /* Verify MQTTSuccess is returned with the following mocks. */ + MQTT_SerializeUnsubscribeHeader_Stub( MQTTV5_SerializeUnsubscribeHeader_cb ); + MQTT_ValidateUnsubscribeProperties_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - MQTT_SerializeUnsubscribeHeader_Stub( MQTT_SerializeSubscribedHeader_cb1 ); - /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, subscribeInfo, 3, MQTT_FIRST_VALID_PACKET_ID ); + /* Expect the above calls when running MQTT_Unsubscribe. */ + + MQTTPropBuilder_t propBuilder = { 0 }; + uint8_t buf[ 500 ]; + size_t bufLength = sizeof( buf ); + propBuilder.pBuffer = buf; + propBuilder.bufferLength = bufLength; + propBuilder.currentIndex = 10; + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, &propBuilder ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + propBuilder.pBuffer = NULL; + MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, &propBuilder ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + propBuilder.pBuffer = buf; + + MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); -} + /*Simulate Failure of MQTT_ValidateUnsubscribeProperties. */ + MQTT_ValidateUnsubscribeProperties_ExpectAnyArgsAndReturn( MQTTBadParameter ); + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, &propBuilder ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); +} void test_MQTT_Unsubscribe_MultipleSubscriptions( void ) { MQTTStatus_t mqttStatus; @@ -6380,6 +7826,7 @@ void test_MQTT_Unsubscribe_MultipleSubscriptions( void ) size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; MQTTPubAckInfo_t incomingRecords = { 0 }; MQTTPubAckInfo_t outgoingRecords = { 0 }; + uint8_t buf[ 100 ]; TEST_ASSERT_EQUAL_MESSAGE( 6U, MQTT_SUB_UNSUB_MAX_VECTORS, "This test is configured to work with MQTT_SUB_UNSUB_MAX_VECTORS defined as 6." ); @@ -6411,7 +7858,7 @@ void test_MQTT_Unsubscribe_MultipleSubscriptions( void ) mqttStatus = MQTT_InitStatefulQoS( &context, &outgoingRecords, 4, - &incomingRecords, 4 ); + &incomingRecords, 4, buf, 0 ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); context.connectStatus = MQTTConnected; @@ -6423,16 +7870,11 @@ void test_MQTT_Unsubscribe_MultipleSubscriptions( void ) MQTT_SerializeUnsubscribeHeader_Stub( MQTT_SerializeUnsubscribedHeader_cb2 ); /* Expect the above calls when running MQTT_Subscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, subscribeInfo, 4, MQTT_FIRST_VALID_PACKET_ID ); + mqttStatus = MQTT_Unsubscribe( &context, subscribeInfo, 4, MQTT_FIRST_VALID_PACKET_ID, NULL ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); } - -/** - * @brief This test case verifies that MQTT_Unsubscribe returns MQTTSendFailed - * if transport interface send returns an error. - */ -void test_MQTT_Unsubscribe_error_path1( void ) +void test_MQTT_Unsubscribe_happy_path_withUP( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; @@ -6442,39 +7884,82 @@ void test_MQTT_Unsubscribe_error_path1( void ) size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; - /* Verify that an error is propagated when transport interface returns an error. */ setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); setupSubscriptionInfo( &subscribeInfo ); subscribeInfo.qos = MQTTQoS0; - - transport.send = transportSendFailure; - transport.recv = transportRecvFailure; - transport.writev = transportWritevFail; - /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - - MQTT_SerializeUnsubscribeHeader_Stub( MQTT_SerializeUnsubscribeHeader_cb ); - /* Verify MQTTSendFailed is propagated when transport interface returns an error. */ + /* Verify MQTTSuccess is returned with the following mocks. */ + MQTT_SerializeUnsubscribeHeader_Stub( MQTTV5_SerializeUnsubscribeHeader_cb ); MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); - /* Expect the above calls when running MQTT_Unsubscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); - /* Case when there is timeout in sending data through transport send. */ + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); +} + +void test_MQTTV5_Unsubscribe_happy_path( void ) +{ + MQTTStatus_t mqttStatus; + MQTTContext_t context = { 0 }; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + MQTTSubscribeInfo_t subscribeInfo = { 0 }; + + size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; + size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; + MQTTPubAckInfo_t incomingRecords = { 0 }; + MQTTPubAckInfo_t outgoingRecords = { 0 }; + + uint8_t ackPropsBuf[ 500 ]; + size_t ackPropsBufLength = sizeof( ackPropsBuf ); + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + setupSubscriptionInfo( &subscribeInfo ); + + /* Initialize context. */ + mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + MQTTPropBuilder_t ackPropsBuffer; + setupackPropsBuilder( &ackPropsBuffer ); + context.ackPropsBuffer = ackPropsBuffer; + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTSuccess ); + mqttStatus = MQTT_InitStatefulQoS( &context, + &outgoingRecords, 4, + &incomingRecords, 4, ackPropsBuf, ackPropsBufLength ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + context.connectStatus = MQTTConnected; + /* Verify MQTTSuccess is returned with the following mocks. */ + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + + /* Expect the above calls when running MQTT_Subscribe. */ + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + /*With NULL buffer of propBuilder. */ + MQTTPropBuilder_t propBuilder; + propBuilder.pBuffer = NULL; + MQTT_GetSubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetSubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + mqttStatus = MQTT_Subscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, &propBuilder ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); } /** - * @brief This test case verifies that MQTT_Unsubscribe returns MQTTSendFailed - * if transport interface send returns an error. + * @brief This test case verifies that MQTT_Unsubscribe does not return success + * when the connection status is anything but MQTTConnected */ -void test_MQTT_Unsubscribe_error_path2( void ) +void test_MQTT_Unsubscribe_not_connected( void ) { MQTTStatus_t mqttStatus; MQTTContext_t context = { 0 }; @@ -6484,38 +7969,39 @@ void test_MQTT_Unsubscribe_error_path2( void ) size_t remainingLength = MQTT_SAMPLE_REMAINING_LENGTH; size_t packetSize = MQTT_SAMPLE_REMAINING_LENGTH; + setupTransportInterface( &transport ); setupNetworkBuffer( &networkBuffer ); setupSubscriptionInfo( &subscribeInfo ); subscribeInfo.qos = MQTTQoS0; - /* Verify that an error is propagated when transport interface returns an error. */ - setupTransportInterface( &transport ); - transport.send = transportSendFailure; - transport.recv = transportRecvFailure; - transport.writev = transportWritevFail; - /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; + /* Test 1 Connection status is MQTTNotConnected*/ + context.connectStatus = MQTTNotConnected; + /* Verify MQTTSuccess is returned with the following mocks. */ + MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); + MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + /* Expect the above calls when running MQTT_Unsubscribe. */ + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTStatusNotConnected, mqttStatus ); - MQTT_SerializeUnsubscribeHeader_Stub( MQTT_SerializeUnsubscribeHeader_cb ); - transport.send = transportSendNoBytes; /* Use the mock function that returns zero bytes sent. */ + /* Test 2 Connection status is MQTTDisconnectPending*/ + context.connectStatus = MQTTDisconnectPending; + /* Verify MQTTSuccess is returned with the following mocks. */ MQTT_GetUnsubscribePacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pPacketSize( &packetSize ); MQTT_GetUnsubscribePacketSize_ReturnThruPtr_pRemainingLength( &remainingLength ); + MQTT_SerializeSubscribeHeader_Stub( MQTTV5_SerializeSubscribedHeader_cb ); /* Expect the above calls when running MQTT_Unsubscribe. */ - mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID ); - TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); + mqttStatus = MQTT_Unsubscribe( &context, &subscribeInfo, 1, MQTT_FIRST_VALID_PACKET_ID, NULL ); + TEST_ASSERT_EQUAL( MQTTStatusDisconnectPending, mqttStatus ); } -/* ========================================================================== */ -/** - * @brief This test case verifies that MQTT_Ping returns MQTTBadParameter - * with context parameter is NULL. - */ void test_MQTT_Ping_invalid_params( void ) { MQTTStatus_t mqttStatus; @@ -6543,9 +8029,7 @@ void test_MQTT_Ping_happy_path( void ) /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - /* Verify MQTTSuccess is returned. */ MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize ); @@ -6598,6 +8082,7 @@ void test_MQTT_Ping_not_connected( void ) TEST_ASSERT_EQUAL( MQTTStatusDisconnectPending, mqttStatus ); } + /** * @brief This test case verifies that MQTT_Ping returns MQTTSendFailed * if transport interface send returns an error. @@ -6621,9 +8106,7 @@ void test_MQTT_Ping_error_path( void ) /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - /* Verify MQTTSendFailed is propagated when transport interface returns an error. */ MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize ); @@ -6638,9 +8121,7 @@ void test_MQTT_Ping_error_path( void ) /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - context.connectStatus = MQTTConnected; - MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess ); MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize ); MQTT_SerializePingreq_ExpectAnyArgsAndReturn( MQTTSuccess ); @@ -6652,9 +8133,6 @@ void test_MQTT_Ping_error_path( void ) /* Initialize context. */ mqttStatus = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer ); TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); - - context.connectStatus = MQTTConnected; - /* Verify MQTTBadParameter is propagated when getting PINGREQ packet size fails. */ MQTT_GetPingreqPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter ); MQTT_GetPingreqPacketSize_ReturnThruPtr_pPacketSize( &pingreqSize ); @@ -6663,8 +8141,6 @@ void test_MQTT_Ping_error_path( void ) TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); } -/* ========================================================================== */ - /** * @brief Test MQTT_MatchTopic for invalid input parameters. */ @@ -7120,8 +8596,6 @@ void test_MQTT_MatchTopic_Wildcard_MultiLevel_No_Match_Cases( void ) TEST_ASSERT_EQUAL( false, matchResult ); } -/* ========================================================================== */ - /** * @brief Tests that MQTT_GetSubAckStatusCodes works as expected in parsing the * payload information of a SUBACK packet. @@ -7184,8 +8658,6 @@ void test_MQTT_GetSubAckStatusCodes( void ) TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); } -/* ========================================================================== */ - /** * @brief Test MQTT_Status_strerror returns correct strings. */ @@ -7242,27 +8714,7 @@ void test_MQTT_Status_strerror( void ) str = MQTT_Status_strerror( status ); TEST_ASSERT_EQUAL_STRING( "MQTTNeedMoreBytes", str ); - status = MQTTStatusConnected; - str = MQTT_Status_strerror( status ); - TEST_ASSERT_EQUAL_STRING( "MQTTStatusConnected", str ); - - status = MQTTStatusNotConnected; - str = MQTT_Status_strerror( status ); - TEST_ASSERT_EQUAL_STRING( "MQTTStatusNotConnected", str ); - - status = MQTTStatusDisconnectPending; - str = MQTT_Status_strerror( status ); - TEST_ASSERT_EQUAL_STRING( "MQTTStatusDisconnectPending", str ); - - status = MQTTPublishStoreFailed; - str = MQTT_Status_strerror( status ); - TEST_ASSERT_EQUAL_STRING( "MQTTPublishStoreFailed", str ); - - status = MQTTPublishRetrieveFailed; - str = MQTT_Status_strerror( status ); - TEST_ASSERT_EQUAL_STRING( "MQTTPublishRetrieveFailed", str ); - - status = MQTTPublishRetrieveFailed + 1; + status = MQTTNeedMoreBytes + 1; str = MQTT_Status_strerror( status ); TEST_ASSERT_EQUAL_STRING( "Invalid MQTT Status code", str ); } @@ -7358,7 +8810,7 @@ void test_MQTT_InitStatefulQoS_fail_null_context( void ) pOutgoingPublishRecords, 10, pIncomingPublishRecords, - 10 ); + 10, NULL, 0 ); TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); } /* ========================================================================== */ @@ -7375,7 +8827,7 @@ void test_MQTT_InitStatefulQoS_zero_outgoing_size( void ) pOutgoingPublishRecords, 0, pIncomingPublishRecords, - 10 ); + 10, NULL, 0 ); TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); } /* ========================================================================== */ @@ -7391,7 +8843,7 @@ void test_MQTT_InitStatefulQoS_zero_incoming_size( void ) pOutgoingPublishRecords, 10, pIncomingPublishRecords, - 0 ); + 0, NULL, 0 ); TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); } /* ========================================================================== */ @@ -7410,11 +8862,58 @@ void test_MQTT_InitStatefulQoS_callback_is_null( void ) pOutgoingPublishRecords, 10, pIncomingPublishRecords, - 10 ); + 10, NULL, 0 ); + TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); + + mqttContext.appCallback = eventCallback2; + mqttStatus = MQTT_InitStatefulQoS( &mqttContext, + pOutgoingPublishRecords, 4, + pIncomingPublishRecords, 4, NULL, 0 ); + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + + uint8_t buffer[ 10 ]; + size_t bufLength = sizeof( buffer ); + MQTT_PropertyBuilder_Init_ExpectAnyArgsAndReturn( MQTTBadParameter ); + mqttStatus = MQTT_InitStatefulQoS( &mqttContext, + pOutgoingPublishRecords, 4, + pIncomingPublishRecords, 4, buffer, bufLength ); TEST_ASSERT_EQUAL( MQTTBadParameter, mqttStatus ); } /* ========================================================================== */ +MQTTStatus_t decode_utf8_Stub( void ) +{ + return MQTTSuccess; +} + +void test_validatePublish( void ) +{ + MQTTStatus_t status = MQTTSuccess; + + status = MQTT_Publish( NULL, NULL, 2, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + MQTTPublishInfo_t publishInfo = { 0 }; + MQTTContext_t context = { 0 }; + publishInfo.qos = 2; + status = MQTT_Publish( &context, &publishInfo, 0, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + publishInfo.qos = 0; + publishInfo.payloadLength = 10; + publishInfo.pPayload = NULL; + status = MQTT_Publish( &context, &publishInfo, 10, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); + + publishInfo.pPayload = "abc"; + publishInfo.payloadLength = 3; + publishInfo.qos = 2; + status = MQTT_Publish( &context, &publishInfo, 10, NULL ); + TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status ); +} + +/* ========================================================================== */ + void test_MQTT_GetBytesInMQTTVec( void ) { TransportOutVector_t pTransportArray[ 10 ] = @@ -7465,3 +8964,38 @@ void test_MQTT_SerializeMQTTVec( void ) TEST_ASSERT_EQUAL_MEMORY( "This is a coreMQTT unit-test string.", array, strlen( "This is a coreMQTT unit-test string." ) ); TEST_ASSERT_EQUAL_MEMORY( "\0\0\0\0\0\0\0\0\0\0\0\0\0", &array[ 37 ], 13 ); } + +void test_receiveFailed( void ) +{ + MQTTContext_t mqttContext = { 0 }; + MQTTConnectInfo_t connectInfo = { 0 }; + MQTTStatus_t status; + TransportInterface_t transport = { 0 }; + MQTTFixedBuffer_t networkBuffer = { 0 }; + + setupTransportInterface( &transport ); + setupNetworkBuffer( &networkBuffer ); + + transport.recv = transportRecvFailure; + + memset( &mqttContext, 0x0, sizeof( mqttContext ) ); + memset( &connectInfo, 0x00, sizeof( connectInfo ) ); + MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer ); + + mqttContext.connectStatus = MQTTConnected; + status = MQTT_ProcessLoop( &mqttContext ); + TEST_ASSERT_EQUAL( MQTTRecvFailed, status ); + TEST_ASSERT_EQUAL( MQTTDisconnectPending, mqttContext.connectStatus ); +} + +void test_MQTT_InitConnect( void ) +{ + MQTTStatus_t status = MQTTSuccess; + + status = MQTT_InitConnect( NULL ); + TEST_ASSERT_EQUAL( MQTTBadParameter, status ); + + MQTTConnectProperties_t connectProperties; + status = MQTT_InitConnect( &connectProperties ); + TEST_ASSERT_EQUAL( MQTTSuccess, status ); +}