├── .travis.yml ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── keywords.txt ├── library.properties └── src ├── AzureIoTProtocol_MQTT.h └── azure_umqtt_c ├── mqtt_client.c ├── mqtt_codec.c └── mqtt_message.c /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | language: generic 5 | env: 6 | global: 7 | - IDE_VERSION=1.8.10 8 | matrix: 9 | - BOARD="esp8266:esp8266:huzzah:FlashSize=4M3M,CpuFrequency=80" 10 | - BOARD="esp8266:esp8266:thing" 11 | - BOARD="esp32:esp32:huzzah" 12 | before_install: 13 | - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 14 | - sleep 3 15 | - export DISPLAY=:1.0 16 | - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz 17 | - tar xf arduino-$IDE_VERSION-linux64.tar.xz 18 | - mv arduino-$IDE_VERSION $HOME/arduino-ide 19 | - export PATH=$PATH:$HOME/arduino-ide 20 | - if [[ "$BOARD" =~ "esp8266:esp8266:" ]]; then 21 | arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --install-boards esp8266:esp8266; 22 | arduino --pref "boardsmanager.additional.urls=" --save-prefs; 23 | arduino --install-library NTPClient; 24 | arduino --install-library NTPClient; 25 | fi 26 | - if [[ "$BOARD" =~ "esp32:esp32:" ]]; then 27 | arduino --pref "boardsmanager.additional.urls=https://dl.espressif.com/dl/package_esp32_index.json" --install-boards esp32:esp32; 28 | arduino --pref "boardsmanager.additional.urls=" --save-prefs; 29 | arduino --install-library NTPClient; 30 | fi 31 | 32 | - findAndReplace() { sed -i'' -e"s|$1|$2|g" "$3"; } 33 | - buildExampleSketch() { 34 | EXAMPLE_SKETCH=$PWD/examples/iothub_ll_telemetry_sample/iothub_ll_telemetry_sample.ino; 35 | 36 | arduino --verbose-build --verify --board $BOARD $EXAMPLE_SKETCH; 37 | } 38 | install: 39 | - arduino --install-library "AzureIoTUtility" 40 | - arduino --install-library "AzureIoTSocket_WiFi" 41 | - arduino --install-library "AzureIoTProtocol_MQTT" 42 | - ln -s $PWD $HOME/Arduino/libraries/. 43 | script: 44 | - buildExampleSketch telemetry_sample.c 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Microsoft Azure IoT SDKs 2 | Copyright (c) Microsoft Corporation 3 | All rights reserved. 4 | MIT License 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the ""Software""), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### Azure IoT Protocol MQTT Library source files 2 | 3 | > ### Stop! Before you proceed: 4 | > 5 | > _This Arduino Library is deprecated._ 6 | > 7 | > _It is kept here for **reference only** and should not be used for any new development._ 8 | > 9 | > _If you’re looking for an Arduino Library you should use the new version: [aka.ms/arduino](https://aka.ms/arduino)_ 10 | > 11 | >_You can find more information about it in this [IoT Tech Community blog post](https://techcommunity.microsoft.com/t5/internet-of-things-blog/arduino-library-for-azure-iot/ba-p/3034455)._ 12 | > 13 | 14 | This is the location of the Arduino-specific source files for the 15 | [AzureIoTProtocol_MQTT Arduino published library](https://github.com/Azure/azure-iot-arduino-protocol-mqtt). 16 | 17 | Complete information for contributing to the Azure IoT Arduino libraries 18 | can be found [here](https://github.com/Azure/azure-iot-pal-arduino). 19 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: ubuntu-latest 11 | 12 | steps: 13 | - script: echo Hello, world! 14 | displayName: 'Run a one-line script' 15 | 16 | - script: | 17 | echo Add other tasks to build, test, and deploy your project. 18 | echo See https://aka.ms/yaml 19 | displayName: 'Run a multi-line script' 20 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For AzureIoTProtocol_MQTT 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | AzureIoTHubClient KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | begin KEYWORD2 16 | setEpochTime KEYWORD2 17 | 18 | ####################################### 19 | # Constants (LITERAL1) 20 | ####################################### 21 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=AzureIoTProtocol_MQTT 2 | version=1.6.1 3 | author=Microsoft 4 | maintainer=Microsoft 5 | sentence=[See deprecation warning!] Azure MQTT protocol library for Arduino. For the Arduino MKR1000 or Zero and WiFi Shield 101, Adafruit Huzzah and Feather M0, or SparkFun Thing. 6 | paragraph=[Warning: This library is being deprecated! Please use Azure SDK for C for new projects]. Microsoft compact implementation of the MQTT protocol for small devices like Arduino. It allows you to use your Arduino with the Azure IoT Hub using MQTT as the transport protocol. See readme.md for more details. 7 | category=Communication 8 | url=https://github.com/Azure/azure-iot-arduino-protocol-mqtt 9 | architectures=samd,esp8266,esp32 10 | -------------------------------------------------------------------------------- /src/AzureIoTProtocol_MQTT.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #ifndef AZUREIOTPROTOCOLMQTT_H 5 | #define AZUREIOTPROTOCOLMQTT_H 6 | 7 | #include "azure_umqtt_c/mqtt_client.h" 8 | 9 | #define AzureIoTProtocolMQTTVersion "1.6.0" 10 | 11 | #endif //AZUREIOTPROTOCOLMQTT_H 12 | -------------------------------------------------------------------------------- /src/azure_umqtt_c/mqtt_client.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include 5 | #include 6 | #include "azure_c_shared_utility/agenttime.h" 7 | #include "azure_c_shared_utility/const_defines.h" 8 | #include "azure_c_shared_utility/crt_abstractions.h" 9 | #include "azure_c_shared_utility/gballoc.h" 10 | #include "azure_c_shared_utility/optimize_size.h" 11 | #include "azure_macro_utils/macro_utils.h" 12 | #include "azure_c_shared_utility/platform.h" 13 | #include "azure_c_shared_utility/strings.h" 14 | #include "azure_c_shared_utility/threadapi.h" 15 | #include "azure_c_shared_utility/tickcounter.h" 16 | #include "azure_c_shared_utility/xlogging.h" 17 | 18 | #include "azure_umqtt_c/mqtt_client.h" 19 | #include "azure_umqtt_c/mqtt_codec.h" 20 | #include 21 | 22 | #define VARIABLE_HEADER_OFFSET 2 23 | #define RETAIN_FLAG_MASK 0x1 24 | #define QOS_LEAST_ONCE_FLAG_MASK 0x2 25 | #define QOS_EXACTLY_ONCE_FLAG_MASK 0x4 26 | #define DUPLICATE_FLAG_MASK 0x8 27 | #define CONNECT_PACKET_MASK 0xf0 28 | #define TIME_MAX_BUFFER 16 29 | #define DEFAULT_MAX_PING_RESPONSE_TIME 80 // % of time to send pings 30 | #define MAX_CLOSE_RETRIES 20 31 | 32 | static const char* const TRUE_CONST = "true"; 33 | static const char* const FALSE_CONST = "false"; 34 | 35 | MU_DEFINE_ENUM_STRINGS(QOS_VALUE, QOS_VALUE_VALUES); 36 | 37 | #define MQTT_STATUS_INITIAL_STATUS 0x0000 38 | #define MQTT_STATUS_CLIENT_CONNECTED 0x0001 39 | #define MQTT_STATUS_SOCKET_CONNECTED 0x0002 40 | #define MQTT_STATUS_PENDING_CLOSE 0x0004 41 | 42 | #define MQTT_FLAGS_LOG_TRACE 0x0001 43 | #define MQTT_FLAGS_RAW_TRACE 0x0002 44 | 45 | typedef struct MQTT_CLIENT_TAG 46 | { 47 | XIO_HANDLE xioHandle; 48 | MQTTCODEC_HANDLE codec_handle; 49 | CONTROL_PACKET_TYPE packetState; 50 | TICK_COUNTER_HANDLE packetTickCntr; 51 | tickcounter_ms_t packetSendTimeMs; 52 | ON_MQTT_OPERATION_CALLBACK fnOperationCallback; 53 | ON_MQTT_MESSAGE_RECV_CALLBACK fnMessageRecv; 54 | void* ctx; 55 | ON_MQTT_ERROR_CALLBACK fnOnErrorCallBack; 56 | void* errorCBCtx; 57 | ON_MQTT_DISCONNECTED_CALLBACK disconnect_cb; 58 | void* disconnect_ctx; 59 | QOS_VALUE qosValue; 60 | uint16_t keepAliveInterval; 61 | MQTT_CLIENT_OPTIONS mqttOptions; 62 | 63 | uint16_t mqtt_status; 64 | uint16_t mqtt_flags; 65 | 66 | tickcounter_ms_t timeSincePing; 67 | uint16_t maxPingRespTime; 68 | } MQTT_CLIENT; 69 | 70 | static bool is_trace_enabled(MQTT_CLIENT* mqtt_client) 71 | { 72 | return (mqtt_client->mqtt_flags & MQTT_FLAGS_LOG_TRACE); 73 | } 74 | 75 | #ifdef ENABLE_RAW_TRACE 76 | static bool is_raw_trace_enabled(MQTT_CLIENT* mqtt_client) 77 | { 78 | return (mqtt_client->mqtt_flags & MQTT_FLAGS_RAW_TRACE); 79 | } 80 | #endif // ENABLE_RAW_TRACE 81 | 82 | static void on_connection_closed(void* context) 83 | { 84 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; 85 | if (mqtt_client != NULL) 86 | { 87 | mqtt_client->mqtt_status &= ~MQTT_STATUS_SOCKET_CONNECTED; 88 | if (mqtt_client->disconnect_cb) 89 | { 90 | mqtt_client->disconnect_cb(mqtt_client->disconnect_ctx); 91 | } 92 | } 93 | } 94 | 95 | static void close_connection(MQTT_CLIENT* mqtt_client) 96 | { 97 | if (mqtt_client->mqtt_status & MQTT_STATUS_SOCKET_CONNECTED) 98 | { 99 | (void)xio_close(mqtt_client->xioHandle, on_connection_closed, mqtt_client); 100 | if (mqtt_client->disconnect_cb == NULL) 101 | { 102 | size_t counter = 0; 103 | do 104 | { 105 | xio_dowork(mqtt_client->xioHandle); 106 | counter++; 107 | ThreadAPI_Sleep(2); 108 | } while (mqtt_client->mqtt_status & MQTT_STATUS_SOCKET_CONNECTED && counter < MAX_CLOSE_RETRIES); 109 | } 110 | // Clear the handle because we don't use it anymore 111 | mqtt_client->xioHandle = NULL; 112 | } 113 | else 114 | { 115 | mqtt_client->mqtt_status &= ~MQTT_STATUS_SOCKET_CONNECTED; 116 | if (mqtt_client->disconnect_cb) 117 | { 118 | mqtt_client->disconnect_cb(mqtt_client->disconnect_ctx); 119 | } 120 | } 121 | } 122 | 123 | static void set_error_callback(MQTT_CLIENT* mqtt_client, MQTT_CLIENT_EVENT_ERROR error_type) 124 | { 125 | if (mqtt_client->fnOnErrorCallBack) 126 | { 127 | mqtt_client->fnOnErrorCallBack(mqtt_client, error_type, mqtt_client->errorCBCtx); 128 | } 129 | close_connection(mqtt_client); 130 | } 131 | 132 | static STRING_HANDLE construct_trace_log_handle(MQTT_CLIENT* mqtt_client) 133 | { 134 | STRING_HANDLE trace_log; 135 | if (is_trace_enabled(mqtt_client) ) 136 | { 137 | trace_log = STRING_new(); 138 | } 139 | else 140 | { 141 | trace_log = NULL; 142 | } 143 | return trace_log; 144 | } 145 | 146 | static uint16_t byteutil_read_uint16(uint8_t** buffer, size_t byteLen) 147 | { 148 | uint16_t result = 0; 149 | if (buffer != NULL && *buffer != NULL && byteLen >= 2) 150 | { 151 | result = 256 * (**buffer) + (*(*buffer + 1)); 152 | *buffer += 2; // Move the ptr 153 | } 154 | else 155 | { 156 | LogError("byteutil_read_uint16 == NULL or less than 2"); 157 | } 158 | return result; 159 | } 160 | 161 | static char* byteutil_readUTF(uint8_t** buffer, size_t* byteLen) 162 | { 163 | char* result = NULL; 164 | 165 | const uint8_t* bufferInitial = *buffer; 166 | // Get the length of the string 167 | uint16_t stringLen = byteutil_read_uint16(buffer, *byteLen); 168 | // Verify that byteutil_read_uint16 succeeded (by stringLen>0) and that we're 169 | // not being asked to read a string longer than buffer passed in. 170 | if ((stringLen > 0) && ((size_t)(stringLen + (*buffer - bufferInitial)) <= *byteLen)) 171 | { 172 | result = (char*)malloc(stringLen + 1); 173 | if (result != NULL) 174 | { 175 | (void)memcpy(result, *buffer, stringLen); 176 | result[stringLen] = '\0'; 177 | *buffer += stringLen; 178 | *byteLen = stringLen; 179 | } 180 | } 181 | else 182 | { 183 | LogError("String passed not a valid UTF."); 184 | } 185 | 186 | return result; 187 | } 188 | 189 | static uint8_t byteutil_readByte(uint8_t** buffer) 190 | { 191 | uint8_t result = 0; 192 | if (buffer != NULL) 193 | { 194 | result = **buffer; 195 | (*buffer)++; 196 | } 197 | else 198 | { 199 | LogError("readByte buffer == NULL."); 200 | } 201 | return result; 202 | } 203 | 204 | static void sendComplete(void* context, IO_SEND_RESULT send_result) 205 | { 206 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; 207 | if (mqtt_client != NULL) 208 | { 209 | if (send_result == IO_SEND_OK) 210 | { 211 | if (mqtt_client->packetState == DISCONNECT_TYPE) 212 | { 213 | /*Codes_SRS_MQTT_CLIENT_07_032: [If the actionResult parameter is of type MQTT_CLIENT_ON_DISCONNECT the the msgInfo value shall be NULL.]*/ 214 | if (mqtt_client->fnOperationCallback != NULL) 215 | { 216 | mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_DISCONNECT, NULL, mqtt_client->ctx); 217 | } 218 | 219 | // Mark to close 220 | mqtt_client->mqtt_status |= MQTT_STATUS_PENDING_CLOSE; 221 | } 222 | } 223 | else if (send_result == IO_SEND_ERROR) 224 | { 225 | LogError("MQTT Send Complete Failure send_result: %d", (int)send_result); 226 | set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR); 227 | } 228 | } 229 | else 230 | { 231 | LogError("MQTT Send Complete Failure with NULL mqtt_client"); 232 | } 233 | } 234 | 235 | #ifndef NO_LOGGING 236 | static void getLogTime(char* timeResult, size_t len) 237 | { 238 | if (timeResult != NULL) 239 | { 240 | time_t agent_time = get_time(NULL); 241 | if (agent_time == (time_t)-1) 242 | { 243 | timeResult[0] = '\0'; 244 | } 245 | else 246 | { 247 | struct tm* tmInfo = localtime(&agent_time); 248 | if (tmInfo == NULL) 249 | { 250 | timeResult[0] = '\0'; 251 | } 252 | else 253 | { 254 | if (strftime(timeResult, len, "%H:%M:%S", tmInfo) == 0) 255 | { 256 | timeResult[0] = '\0'; 257 | } 258 | } 259 | } 260 | } 261 | } 262 | 263 | #ifdef ENABLE_RAW_TRACE 264 | static const char* retrievePacketType(CONTROL_PACKET_TYPE packet) 265 | { 266 | switch (packet&CONNECT_PACKET_MASK) 267 | { 268 | case CONNECT_TYPE: return "CONNECT"; 269 | case CONNACK_TYPE: return "CONNACK"; 270 | case PUBLISH_TYPE: return "PUBLISH"; 271 | case PUBACK_TYPE: return "PUBACK"; 272 | case PUBREC_TYPE: return "PUBREC"; 273 | case PUBREL_TYPE: return "PUBREL"; 274 | case SUBSCRIBE_TYPE: return "SUBSCRIBE"; 275 | case SUBACK_TYPE: return "SUBACK"; 276 | case UNSUBSCRIBE_TYPE: return "UNSUBSCRIBE"; 277 | case UNSUBACK_TYPE: return "UNSUBACK"; 278 | case PINGREQ_TYPE: return "PINGREQ"; 279 | case PINGRESP_TYPE: return "PINGRESP"; 280 | case DISCONNECT_TYPE: return "DISCONNECT"; 281 | default: 282 | case PACKET_TYPE_ERROR: 283 | case UNKNOWN_TYPE: 284 | return "UNKNOWN"; 285 | } 286 | } 287 | 288 | static void logOutgoingRawTrace(MQTT_CLIENT* mqtt_client, const uint8_t* data, size_t length) 289 | { 290 | if (mqtt_client != NULL && data != NULL && length > 0 && is_raw_trace_enabled(mqtt_client)) 291 | { 292 | char tmBuffer[TIME_MAX_BUFFER]; 293 | getLogTime(tmBuffer, TIME_MAX_BUFFER); 294 | 295 | LOG(AZ_LOG_TRACE, 0, "-> %s %s: ", tmBuffer, retrievePacketType((unsigned char)data[0])); 296 | size_t index = 0; 297 | for (index = 0; index < length; index++) 298 | { 299 | LOG(AZ_LOG_TRACE, 0, "0x%02x ", data[index]); 300 | } 301 | LOG(AZ_LOG_TRACE, LOG_LINE, " "); 302 | } 303 | } 304 | 305 | static void logIncomingRawTrace(MQTT_CLIENT* mqtt_client, CONTROL_PACKET_TYPE packet, uint8_t flags, const uint8_t* data, size_t length) 306 | { 307 | if (mqtt_client != NULL && is_raw_trace_enabled(mqtt_client)) 308 | { 309 | if (data != NULL && length > 0) 310 | { 311 | char tmBuffer[TIME_MAX_BUFFER]; 312 | getLogTime(tmBuffer, TIME_MAX_BUFFER); 313 | 314 | LOG(AZ_LOG_TRACE, 0, "<- %s %s: 0x%02x 0x%02x ", tmBuffer, retrievePacketType((CONTROL_PACKET_TYPE)packet), (unsigned int)(packet | flags), (unsigned int)length); 315 | size_t index = 0; 316 | for (index = 0; index < length; index++) 317 | { 318 | LOG(AZ_LOG_TRACE, 0, "0x%02x ", data[index]); 319 | } 320 | LOG(AZ_LOG_TRACE, LOG_LINE, " "); 321 | } 322 | else if (packet == PINGRESP_TYPE) 323 | { 324 | char tmBuffer[TIME_MAX_BUFFER]; 325 | getLogTime(tmBuffer, TIME_MAX_BUFFER); 326 | LOG(AZ_LOG_TRACE, LOG_LINE, "<- %s %s: 0x%02x 0x%02x ", tmBuffer, retrievePacketType((CONTROL_PACKET_TYPE)packet), (unsigned int)(packet | flags), (unsigned int)length); 327 | } 328 | } 329 | } 330 | #endif // ENABLE_RAW_TRACE 331 | 332 | static void log_outgoing_trace(MQTT_CLIENT* mqtt_client, STRING_HANDLE trace_log) 333 | { 334 | if (mqtt_client != NULL && is_trace_enabled(mqtt_client) && trace_log != NULL) 335 | { 336 | char tmBuffer[TIME_MAX_BUFFER]; 337 | getLogTime(tmBuffer, TIME_MAX_BUFFER); 338 | LOG(AZ_LOG_TRACE, LOG_LINE, "-> %s %s", tmBuffer, STRING_c_str(trace_log)); 339 | } 340 | } 341 | 342 | static void log_incoming_trace(MQTT_CLIENT* mqtt_client, STRING_HANDLE trace_log) 343 | { 344 | if (mqtt_client != NULL && is_trace_enabled(mqtt_client) && trace_log != NULL) 345 | { 346 | char tmBuffer[TIME_MAX_BUFFER]; 347 | getLogTime(tmBuffer, TIME_MAX_BUFFER); 348 | LOG(AZ_LOG_TRACE, LOG_LINE, "<- %s %s", tmBuffer, STRING_c_str(trace_log) ); 349 | } 350 | } 351 | #else // NO_LOGGING 352 | static void logOutgoingRawTrace(MQTT_CLIENT* mqtt_client, const uint8_t* data, size_t length) 353 | { 354 | AZURE_UNREFERENCED_PARAMETER(mqtt_client); 355 | AZURE_UNREFERENCED_PARAMETER(data); 356 | AZURE_UNREFERENCED_PARAMETER(length); 357 | } 358 | 359 | static void log_outgoing_trace(MQTT_CLIENT* mqtt_client, STRING_HANDLE trace_log) 360 | { 361 | AZURE_UNREFERENCED_PARAMETER(mqtt_client); 362 | AZURE_UNREFERENCED_PARAMETER(trace_log); 363 | } 364 | 365 | static void log_incoming_trace(MQTT_CLIENT* mqtt_client, STRING_HANDLE trace_log) 366 | { 367 | AZURE_UNREFERENCED_PARAMETER(mqtt_client); 368 | AZURE_UNREFERENCED_PARAMETER(trace_log); 369 | } 370 | 371 | static void logIncomingRawTrace(MQTT_CLIENT* mqtt_client, CONTROL_PACKET_TYPE packet, uint8_t flags, const uint8_t* data, size_t length) 372 | { 373 | AZURE_UNREFERENCED_PARAMETER(mqtt_client); 374 | AZURE_UNREFERENCED_PARAMETER(packet); 375 | AZURE_UNREFERENCED_PARAMETER(flags); 376 | AZURE_UNREFERENCED_PARAMETER(data); 377 | AZURE_UNREFERENCED_PARAMETER(length); 378 | } 379 | #endif // NO_LOGGING 380 | 381 | static int sendPacketItem(MQTT_CLIENT* mqtt_client, const unsigned char* data, size_t length) 382 | { 383 | int result; 384 | 385 | if (tickcounter_get_current_ms(mqtt_client->packetTickCntr, &mqtt_client->packetSendTimeMs) != 0) 386 | { 387 | LogError("Failure getting current ms tickcounter"); 388 | result = MU_FAILURE; 389 | } 390 | else 391 | { 392 | result = xio_send(mqtt_client->xioHandle, (const void*)data, length, sendComplete, mqtt_client); 393 | if (result != 0) 394 | { 395 | LogError("Failure sending control packet data"); 396 | result = MU_FAILURE; 397 | } 398 | else 399 | { 400 | #ifdef ENABLE_RAW_TRACE 401 | logOutgoingRawTrace(mqtt_client, (const uint8_t*)data, length); 402 | #endif 403 | } 404 | } 405 | return result; 406 | } 407 | 408 | static void onOpenComplete(void* context, IO_OPEN_RESULT open_result) 409 | { 410 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; 411 | if (mqtt_client != NULL) 412 | { 413 | if (open_result == IO_OPEN_OK && !(mqtt_client->mqtt_status & MQTT_STATUS_SOCKET_CONNECTED)) 414 | { 415 | mqtt_client->packetState = CONNECT_TYPE; 416 | mqtt_client->mqtt_status |= MQTT_STATUS_SOCKET_CONNECTED; 417 | 418 | STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); 419 | 420 | // Send the Connect packet 421 | BUFFER_HANDLE connPacket = mqtt_codec_connect(&mqtt_client->mqttOptions, trace_log); 422 | if (connPacket == NULL) 423 | { 424 | LogError("Error: mqtt_codec_connect failed"); 425 | } 426 | else 427 | { 428 | size_t size = BUFFER_length(connPacket); 429 | /*Codes_SRS_MQTT_CLIENT_07_009: [On success mqtt_client_connect shall send the MQTT CONNECT to the endpoint.]*/ 430 | if (sendPacketItem(mqtt_client, BUFFER_u_char(connPacket), size) != 0) 431 | { 432 | LogError("Error: failure sending connect packet"); 433 | // Set the status to pending close because we connot continue 434 | // with CONN failing to send 435 | if (mqtt_client->fnOnErrorCallBack) 436 | { 437 | mqtt_client->fnOnErrorCallBack(mqtt_client, MQTT_CLIENT_CONNECTION_ERROR, mqtt_client->errorCBCtx); 438 | } 439 | mqtt_client->mqtt_status |= MQTT_STATUS_PENDING_CLOSE; 440 | } 441 | else 442 | { 443 | log_outgoing_trace(mqtt_client, trace_log); 444 | } 445 | BUFFER_delete(connPacket); 446 | } 447 | if (trace_log != NULL) 448 | { 449 | STRING_delete(trace_log); 450 | } 451 | } 452 | else 453 | { 454 | LogError("Error: failure opening connection to endpoint"); 455 | if (!(mqtt_client->mqtt_status & MQTT_STATUS_SOCKET_CONNECTED) && mqtt_client->fnOnErrorCallBack) 456 | { 457 | mqtt_client->fnOnErrorCallBack(mqtt_client, MQTT_CLIENT_CONNECTION_ERROR, mqtt_client->errorCBCtx); 458 | } 459 | mqtt_client->mqtt_status |= MQTT_STATUS_PENDING_CLOSE; 460 | } 461 | } 462 | else 463 | { 464 | LogError("Error: mqtt_client is NULL"); 465 | } 466 | } 467 | 468 | static void onBytesReceived(void* context, const unsigned char* buffer, size_t size) 469 | { 470 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; 471 | if (mqtt_client != NULL) 472 | { 473 | if (mqtt_codec_bytesReceived(mqtt_client->codec_handle, buffer, size) != 0) 474 | { 475 | mqtt_codec_reset(mqtt_client->codec_handle); 476 | set_error_callback(mqtt_client, MQTT_CLIENT_PARSE_ERROR); 477 | } 478 | } 479 | else 480 | { 481 | LogError("Error: mqtt_client is NULL"); 482 | } 483 | } 484 | 485 | static void onIoError(void* context) 486 | { 487 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; 488 | if (mqtt_client != NULL && mqtt_client->fnOperationCallback) 489 | { 490 | /*Codes_SRS_MQTT_CLIENT_07_032: [If the actionResult parameter is of type MQTT_CLIENT_ON_DISCONNECT the the msgInfo value shall be NULL.]*/ 491 | /* Codes_SRS_MQTT_CLIENT_07_036: [ If an error is encountered by the ioHandle the mqtt_client shall call xio_close. ] */ 492 | set_error_callback(mqtt_client, MQTT_CLIENT_CONNECTION_ERROR); 493 | } 494 | else 495 | { 496 | LogError("Error invalid parameter: mqtt_client: %p", mqtt_client); 497 | } 498 | } 499 | 500 | static void clear_mqtt_options(MQTT_CLIENT* mqtt_client) 501 | { 502 | if (mqtt_client->mqttOptions.clientId != NULL) 503 | { 504 | free(mqtt_client->mqttOptions.clientId); 505 | mqtt_client->mqttOptions.clientId = NULL; 506 | } 507 | 508 | if (mqtt_client->mqttOptions.willTopic != NULL) 509 | { 510 | free(mqtt_client->mqttOptions.willTopic); 511 | mqtt_client->mqttOptions.willTopic = NULL; 512 | } 513 | 514 | if (mqtt_client->mqttOptions.willMessage != NULL) 515 | { 516 | free(mqtt_client->mqttOptions.willMessage); 517 | mqtt_client->mqttOptions.willMessage = NULL; 518 | } 519 | 520 | if (mqtt_client->mqttOptions.username != NULL) 521 | { 522 | free(mqtt_client->mqttOptions.username); 523 | mqtt_client->mqttOptions.username = NULL; 524 | } 525 | 526 | if (mqtt_client->mqttOptions.password != NULL) 527 | { 528 | free(mqtt_client->mqttOptions.password); 529 | mqtt_client->mqttOptions.password = NULL; 530 | } 531 | } 532 | 533 | static int cloneMqttOptions(MQTT_CLIENT* mqtt_client, const MQTT_CLIENT_OPTIONS* mqttOptions) 534 | { 535 | int result = 0; 536 | char* temp_option; 537 | 538 | if (mqttOptions->clientId != NULL) 539 | { 540 | if (mallocAndStrcpy_s(&temp_option, mqttOptions->clientId) != 0) 541 | { 542 | result = MU_FAILURE; 543 | LogError("mallocAndStrcpy_s clientId"); 544 | } 545 | else 546 | { 547 | if (mqtt_client->mqttOptions.clientId != NULL) 548 | { 549 | free(mqtt_client->mqttOptions.clientId); 550 | } 551 | mqtt_client->mqttOptions.clientId = temp_option; 552 | } 553 | } 554 | if (result == 0 && mqttOptions->willTopic != NULL) 555 | { 556 | temp_option = NULL; 557 | if (mallocAndStrcpy_s(&temp_option, mqttOptions->willTopic) != 0) 558 | { 559 | result = MU_FAILURE; 560 | LogError("mallocAndStrcpy_s willTopic"); 561 | } 562 | else 563 | { 564 | if (mqtt_client->mqttOptions.willTopic != NULL) 565 | { 566 | free(mqtt_client->mqttOptions.willTopic); 567 | } 568 | mqtt_client->mqttOptions.willTopic = temp_option; 569 | } 570 | } 571 | if (result == 0 && mqttOptions->willMessage != NULL) 572 | { 573 | temp_option = NULL; 574 | if (mallocAndStrcpy_s(&temp_option, mqttOptions->willMessage) != 0) 575 | { 576 | LogError("mallocAndStrcpy_s willMessage"); 577 | result = MU_FAILURE; 578 | } 579 | else 580 | { 581 | if (mqtt_client->mqttOptions.willMessage != NULL) 582 | { 583 | free(mqtt_client->mqttOptions.willMessage); 584 | } 585 | mqtt_client->mqttOptions.willMessage = temp_option; 586 | } 587 | } 588 | if (result == 0 && mqttOptions->username != NULL) 589 | { 590 | temp_option = NULL; 591 | if (mallocAndStrcpy_s(&temp_option, mqttOptions->username) != 0) 592 | { 593 | LogError("mallocAndStrcpy_s username"); 594 | result = MU_FAILURE; 595 | } 596 | else 597 | { 598 | if (mqtt_client->mqttOptions.username != NULL) 599 | { 600 | free(mqtt_client->mqttOptions.username); 601 | } 602 | mqtt_client->mqttOptions.username = temp_option; 603 | } 604 | } 605 | if (result == 0 && mqttOptions->password != NULL) 606 | { 607 | temp_option = NULL; 608 | if (mallocAndStrcpy_s(&temp_option, mqttOptions->password) != 0) 609 | { 610 | LogError("mallocAndStrcpy_s password"); 611 | result = MU_FAILURE; 612 | } 613 | else 614 | { 615 | if (mqtt_client->mqttOptions.password != NULL) 616 | { 617 | free(mqtt_client->mqttOptions.password); 618 | } 619 | mqtt_client->mqttOptions.password = temp_option; 620 | } 621 | } 622 | if (result == 0) 623 | { 624 | mqtt_client->mqttOptions.keepAliveInterval = mqttOptions->keepAliveInterval; 625 | mqtt_client->mqttOptions.messageRetain = mqttOptions->messageRetain; 626 | mqtt_client->mqttOptions.useCleanSession = mqttOptions->useCleanSession; 627 | mqtt_client->mqttOptions.qualityOfServiceValue = mqttOptions->qualityOfServiceValue; 628 | } 629 | else 630 | { 631 | clear_mqtt_options(mqtt_client); 632 | } 633 | return result; 634 | } 635 | 636 | static void ProcessPublishMessage(MQTT_CLIENT* mqtt_client, uint8_t* initialPos, size_t packetLength, int flags) 637 | { 638 | bool isDuplicateMsg = (flags & DUPLICATE_FLAG_MASK) ? true : false; 639 | bool isRetainMsg = (flags & RETAIN_FLAG_MASK) ? true : false; 640 | QOS_VALUE qosValue = (flags == 0) ? DELIVER_AT_MOST_ONCE : (flags & QOS_LEAST_ONCE_FLAG_MASK) ? DELIVER_AT_LEAST_ONCE : DELIVER_EXACTLY_ONCE; 641 | 642 | uint8_t* iterator = initialPos; 643 | size_t numberOfBytesToBeRead = packetLength; 644 | size_t lengthOfTopicName = numberOfBytesToBeRead; 645 | char* topicName = byteutil_readUTF(&iterator, &lengthOfTopicName); 646 | if (topicName == NULL) 647 | { 648 | LogError("Publish MSG: failure reading topic name"); 649 | set_error_callback(mqtt_client, MQTT_CLIENT_PARSE_ERROR); 650 | } 651 | else 652 | { 653 | STRING_HANDLE trace_log = NULL; 654 | 655 | #ifndef NO_LOGGING 656 | if (is_trace_enabled(mqtt_client)) 657 | { 658 | trace_log = STRING_construct_sprintf("PUBLISH | IS_DUP: %s | RETAIN: %d | QOS: %s | TOPIC_NAME: %s", isDuplicateMsg ? TRUE_CONST : FALSE_CONST, 659 | isRetainMsg ? 1 : 0, MU_ENUM_TO_STRING(QOS_VALUE, qosValue), topicName); 660 | } 661 | #endif 662 | uint16_t packetId = 0; 663 | numberOfBytesToBeRead = packetLength - (iterator - initialPos); 664 | if (qosValue != DELIVER_AT_MOST_ONCE) 665 | { 666 | packetId = byteutil_read_uint16(&iterator, numberOfBytesToBeRead); 667 | #ifndef NO_LOGGING 668 | if (is_trace_enabled(mqtt_client)) 669 | { 670 | STRING_sprintf(trace_log, " | PACKET_ID: %"PRIu16, packetId); 671 | } 672 | #endif 673 | } 674 | if ((qosValue != DELIVER_AT_MOST_ONCE) && (packetId == 0)) 675 | { 676 | LogError("Publish MSG: packetId=0, invalid"); 677 | set_error_callback(mqtt_client, MQTT_CLIENT_PARSE_ERROR); 678 | } 679 | else 680 | { 681 | numberOfBytesToBeRead = packetLength - (iterator - initialPos); 682 | 683 | MQTT_MESSAGE_HANDLE msgHandle = mqttmessage_create_in_place(packetId, topicName, qosValue, iterator, numberOfBytesToBeRead); 684 | if (msgHandle == NULL) 685 | { 686 | LogError("failure in mqttmessage_create"); 687 | set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); 688 | } 689 | else if (mqttmessage_setIsDuplicateMsg(msgHandle, isDuplicateMsg) != 0 || 690 | mqttmessage_setIsRetained(msgHandle, isRetainMsg) != 0) 691 | { 692 | LogError("failure setting mqtt message property"); 693 | set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); 694 | } 695 | else 696 | { 697 | #ifndef NO_LOGGING 698 | if (is_trace_enabled(mqtt_client)) 699 | { 700 | STRING_sprintf(trace_log, " | PAYLOAD_LEN: %lu", (unsigned long)numberOfBytesToBeRead); 701 | log_incoming_trace(mqtt_client, trace_log); 702 | } 703 | #endif 704 | mqtt_client->fnMessageRecv(msgHandle, mqtt_client->ctx); 705 | 706 | BUFFER_HANDLE pubRel = NULL; 707 | if (qosValue == DELIVER_EXACTLY_ONCE) 708 | { 709 | pubRel = mqtt_codec_publishReceived(packetId); 710 | if (pubRel == NULL) 711 | { 712 | LogError("Failed to allocate publish receive message."); 713 | set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); 714 | } 715 | } 716 | else if (qosValue == DELIVER_AT_LEAST_ONCE) 717 | { 718 | pubRel = mqtt_codec_publishAck(packetId); 719 | if (pubRel == NULL) 720 | { 721 | LogError("Failed to allocate publish ack message."); 722 | set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); 723 | } 724 | } 725 | if (pubRel != NULL) 726 | { 727 | size_t size = BUFFER_length(pubRel); 728 | if (sendPacketItem(mqtt_client, BUFFER_u_char(pubRel), size) != 0) 729 | { 730 | LogError("Failed sending publish reply."); 731 | set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR); 732 | } 733 | BUFFER_delete(pubRel); 734 | } 735 | } 736 | mqttmessage_destroy(msgHandle); 737 | } 738 | 739 | if (trace_log != NULL) 740 | { 741 | STRING_delete(trace_log); 742 | } 743 | 744 | free(topicName); 745 | } 746 | } 747 | 748 | static void recvCompleteCallback(void* context, CONTROL_PACKET_TYPE packet, int flags, BUFFER_HANDLE headerData) 749 | { 750 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)context; 751 | if (mqtt_client != NULL) 752 | { 753 | size_t packetLength = 0; 754 | uint8_t* iterator = NULL; 755 | if (headerData != NULL) 756 | { 757 | packetLength = BUFFER_length(headerData); 758 | iterator = BUFFER_u_char(headerData); 759 | } 760 | 761 | #ifdef ENABLE_RAW_TRACE 762 | logIncomingRawTrace(mqtt_client, packet, (uint8_t)flags, iterator, packetLength); 763 | #endif 764 | if ((iterator != NULL && packetLength > 0) || packet == PINGRESP_TYPE) 765 | { 766 | switch (packet) 767 | { 768 | case CONNACK_TYPE: 769 | { 770 | /*Codes_SRS_MQTT_CLIENT_07_028: [If the actionResult parameter is of type CONNECT_ACK then the msgInfo value shall be a CONNECT_ACK structure.]*/ 771 | CONNECT_ACK connack = { 0 }; 772 | connack.isSessionPresent = (byteutil_readByte(&iterator) == 0x1) ? true : false; 773 | uint8_t rc = byteutil_readByte(&iterator); 774 | connack.returnCode = 775 | (rc < ((uint8_t)CONN_REFUSED_UNKNOWN)) ? 776 | (CONNECT_RETURN_CODE)rc : CONN_REFUSED_UNKNOWN; 777 | 778 | #ifndef NO_LOGGING 779 | if (is_trace_enabled(mqtt_client)) 780 | { 781 | STRING_HANDLE trace_log = STRING_construct_sprintf("CONNACK | SESSION_PRESENT: %s | RETURN_CODE: 0x%x", connack.isSessionPresent ? TRUE_CONST : FALSE_CONST, connack.returnCode); 782 | log_incoming_trace(mqtt_client, trace_log); 783 | STRING_delete(trace_log); 784 | } 785 | #endif 786 | mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_CONNACK, (void*)&connack, mqtt_client->ctx); 787 | 788 | if (connack.returnCode == CONNECTION_ACCEPTED) 789 | { 790 | mqtt_client->mqtt_status |= MQTT_STATUS_CLIENT_CONNECTED; 791 | } 792 | break; 793 | } 794 | case PUBLISH_TYPE: 795 | { 796 | ProcessPublishMessage(mqtt_client, iterator, packetLength, flags); 797 | break; 798 | } 799 | case PUBACK_TYPE: 800 | case PUBREC_TYPE: 801 | case PUBREL_TYPE: 802 | case PUBCOMP_TYPE: 803 | { 804 | /*Codes_SRS_MQTT_CLIENT_07_029: [If the actionResult parameter are of types PUBACK_TYPE, PUBREC_TYPE, PUBREL_TYPE or PUBCOMP_TYPE then the msgInfo value shall be a PUBLISH_ACK structure.]*/ 805 | MQTT_CLIENT_EVENT_RESULT action = (packet == PUBACK_TYPE) ? MQTT_CLIENT_ON_PUBLISH_ACK : 806 | (packet == PUBREC_TYPE) ? MQTT_CLIENT_ON_PUBLISH_RECV : 807 | (packet == PUBREL_TYPE) ? MQTT_CLIENT_ON_PUBLISH_REL : MQTT_CLIENT_ON_PUBLISH_COMP; 808 | 809 | PUBLISH_ACK publish_ack = { 0 }; 810 | publish_ack.packetId = byteutil_read_uint16(&iterator, packetLength); 811 | 812 | #ifndef NO_LOGGING 813 | if (is_trace_enabled(mqtt_client)) 814 | { 815 | STRING_HANDLE trace_log = STRING_construct_sprintf("%s | PACKET_ID: %"PRIu16, packet == PUBACK_TYPE ? "PUBACK" : (packet == PUBREC_TYPE) ? "PUBREC" : (packet == PUBREL_TYPE) ? "PUBREL" : "PUBCOMP", 816 | publish_ack.packetId); 817 | 818 | log_incoming_trace(mqtt_client, trace_log); 819 | STRING_delete(trace_log); 820 | } 821 | #endif 822 | BUFFER_HANDLE pubRel = NULL; 823 | mqtt_client->fnOperationCallback(mqtt_client, action, (void*)&publish_ack, mqtt_client->ctx); 824 | if (packet == PUBREC_TYPE) 825 | { 826 | pubRel = mqtt_codec_publishRelease(publish_ack.packetId); 827 | if (pubRel == NULL) 828 | { 829 | LogError("Failed to allocate publish release message."); 830 | set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); 831 | } 832 | } 833 | else if (packet == PUBREL_TYPE) 834 | { 835 | pubRel = mqtt_codec_publishComplete(publish_ack.packetId); 836 | if (pubRel == NULL) 837 | { 838 | LogError("Failed to allocate publish complete message."); 839 | set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); 840 | } 841 | } 842 | if (pubRel != NULL) 843 | { 844 | size_t size = BUFFER_length(pubRel); 845 | if (sendPacketItem(mqtt_client, BUFFER_u_char(pubRel), size) != 0) 846 | { 847 | LogError("Failed sending publish reply."); 848 | set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR); 849 | } 850 | BUFFER_delete(pubRel); 851 | } 852 | break; 853 | } 854 | case SUBACK_TYPE: 855 | { 856 | 857 | /*Codes_SRS_MQTT_CLIENT_07_030: [If the actionResult parameter is of type SUBACK_TYPE then the msgInfo value shall be a SUBSCRIBE_ACK structure.]*/ 858 | SUBSCRIBE_ACK suback = { 0 }; 859 | 860 | size_t remainLen = packetLength; 861 | suback.packetId = byteutil_read_uint16(&iterator, packetLength); 862 | remainLen -= 2; 863 | 864 | #ifndef NO_LOGGING 865 | STRING_HANDLE trace_log = NULL; 866 | if (is_trace_enabled(mqtt_client)) 867 | { 868 | trace_log = STRING_construct_sprintf("SUBACK | PACKET_ID: %"PRIu16, suback.packetId); 869 | } 870 | #endif 871 | // Allocate the remaining len 872 | suback.qosReturn = (QOS_VALUE*)malloc(sizeof(QOS_VALUE)*remainLen); 873 | if (suback.qosReturn != NULL) 874 | { 875 | while (remainLen > 0) 876 | { 877 | uint8_t qosRet = byteutil_readByte(&iterator); 878 | suback.qosReturn[suback.qosCount++] = 879 | (qosRet <= ((uint8_t)DELIVER_EXACTLY_ONCE)) ? 880 | (QOS_VALUE)qosRet : DELIVER_FAILURE; 881 | remainLen--; 882 | #ifndef NO_LOGGING 883 | if (is_trace_enabled(mqtt_client)) 884 | { 885 | STRING_sprintf(trace_log, " | RETURN_CODE: %"PRIu16, suback.qosReturn[suback.qosCount-1]); 886 | } 887 | #endif 888 | } 889 | 890 | #ifndef NO_LOGGING 891 | if (is_trace_enabled(mqtt_client)) 892 | { 893 | log_incoming_trace(mqtt_client, trace_log); 894 | STRING_delete(trace_log); 895 | } 896 | #endif 897 | mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_SUBSCRIBE_ACK, (void*)&suback, mqtt_client->ctx); 898 | free(suback.qosReturn); 899 | } 900 | else 901 | { 902 | LogError("allocation of quality of service value failed."); 903 | set_error_callback(mqtt_client, MQTT_CLIENT_MEMORY_ERROR); 904 | } 905 | break; 906 | } 907 | case UNSUBACK_TYPE: 908 | { 909 | /*Codes_SRS_MQTT_CLIENT_07_031: [If the actionResult parameter is of type UNSUBACK_TYPE then the msgInfo value shall be a UNSUBSCRIBE_ACK structure.]*/ 910 | UNSUBSCRIBE_ACK unsuback = { 0 }; 911 | unsuback.packetId = byteutil_read_uint16(&iterator, packetLength); 912 | 913 | #ifndef NO_LOGGING 914 | if (is_trace_enabled(mqtt_client)) 915 | { 916 | STRING_HANDLE trace_log = STRING_construct_sprintf("UNSUBACK | PACKET_ID: %"PRIu16, unsuback.packetId); 917 | log_incoming_trace(mqtt_client, trace_log); 918 | STRING_delete(trace_log); 919 | } 920 | #endif 921 | mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_UNSUBSCRIBE_ACK, (void*)&unsuback, mqtt_client->ctx); 922 | break; 923 | } 924 | case PINGRESP_TYPE: 925 | mqtt_client->timeSincePing = 0; 926 | #ifndef NO_LOGGING 927 | if (is_trace_enabled(mqtt_client)) 928 | { 929 | STRING_HANDLE trace_log = STRING_construct_sprintf("PINGRESP"); 930 | log_incoming_trace(mqtt_client, trace_log); 931 | STRING_delete(trace_log); 932 | } 933 | #endif 934 | // Forward ping response to operation callback 935 | mqtt_client->fnOperationCallback(mqtt_client, MQTT_CLIENT_ON_PING_RESPONSE, NULL, mqtt_client->ctx); 936 | break; 937 | default: 938 | break; 939 | } 940 | } 941 | } 942 | else 943 | { 944 | LogError("recvCompleteCallback context failed."); 945 | } 946 | } 947 | 948 | void mqtt_client_clear_xio(MQTT_CLIENT_HANDLE handle) 949 | { 950 | if (handle != NULL) 951 | { 952 | MQTT_CLIENT *mqtt_client = (MQTT_CLIENT *)handle; 953 | 954 | // The upstream transport handle allocates and deallocates the xio handle. 955 | // The reference to that xio handle is shared between this layer (mqtt layer) 956 | // and the upstream layer. The clearing done here is to signal to this layer 957 | // that the handle is no longer available to be used. This is different from 958 | // deiniting the mqtt client in that we do not want an entire teardown, but 959 | // only a possible re-upping of the xio in the future. 960 | mqtt_client->xioHandle = NULL; 961 | } 962 | } 963 | 964 | MQTT_CLIENT_HANDLE mqtt_client_init(ON_MQTT_MESSAGE_RECV_CALLBACK msgRecv, ON_MQTT_OPERATION_CALLBACK operation_cb, void* opCallbackCtx, ON_MQTT_ERROR_CALLBACK onErrorCallBack, void* errorCBCtx) 965 | { 966 | MQTT_CLIENT* result; 967 | /*Codes_SRS_MQTT_CLIENT_07_001: [If the parameters ON_MQTT_MESSAGE_RECV_CALLBACK is NULL then mqttclient_init shall return NULL.]*/ 968 | if (msgRecv == NULL || operation_cb == NULL) 969 | { 970 | LogError("Invalid parameter specified msgRecv: %p, operation_cb: %p", msgRecv, operation_cb); 971 | result = NULL; 972 | } 973 | else 974 | { 975 | result = malloc(sizeof(MQTT_CLIENT)); 976 | if (result == NULL) 977 | { 978 | /*Codes_SRS_MQTT_CLIENT_07_002: [If any failure is encountered then mqttclient_init shall return NULL.]*/ 979 | LogError("mqtt_client_init failure: Allocation Failure"); 980 | } 981 | else 982 | { 983 | memset(result, 0, sizeof(MQTT_CLIENT)); 984 | /*Codes_SRS_MQTT_CLIENT_07_003: [mqttclient_init shall allocate MQTTCLIENT_DATA_INSTANCE and return the MQTTCLIENT_HANDLE on success.]*/ 985 | result->packetState = UNKNOWN_TYPE; 986 | result->fnOperationCallback = operation_cb; 987 | result->ctx = opCallbackCtx; 988 | result->fnMessageRecv = msgRecv; 989 | result->fnOnErrorCallBack = onErrorCallBack; 990 | result->errorCBCtx = errorCBCtx; 991 | result->qosValue = DELIVER_AT_MOST_ONCE; 992 | result->packetTickCntr = tickcounter_create(); 993 | result->maxPingRespTime = DEFAULT_MAX_PING_RESPONSE_TIME; 994 | if (result->packetTickCntr == NULL) 995 | { 996 | /*Codes_SRS_MQTT_CLIENT_07_002: [If any failure is encountered then mqttclient_init shall return NULL.]*/ 997 | LogError("mqtt_client_init failure: tickcounter_create failure"); 998 | free(result); 999 | result = NULL; 1000 | } 1001 | else 1002 | { 1003 | result->codec_handle = mqtt_codec_create(recvCompleteCallback, result); 1004 | if (result->codec_handle == NULL) 1005 | { 1006 | /*Codes_SRS_MQTT_CLIENT_07_002: [If any failure is encountered then mqttclient_init shall return NULL.]*/ 1007 | LogError("mqtt_client_init failure: mqtt_codec_create failure"); 1008 | tickcounter_destroy(result->packetTickCntr); 1009 | free(result); 1010 | result = NULL; 1011 | } 1012 | } 1013 | } 1014 | } 1015 | return result; 1016 | } 1017 | 1018 | void mqtt_client_deinit(MQTT_CLIENT_HANDLE handle) 1019 | { 1020 | /*Codes_SRS_MQTT_CLIENT_07_004: [If the parameter handle is NULL then function mqtt_client_deinit shall do nothing.]*/ 1021 | if (handle != NULL) 1022 | { 1023 | /*Codes_SRS_MQTT_CLIENT_07_005: [mqtt_client_deinit shall deallocate all memory allocated in this unit.]*/ 1024 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; 1025 | tickcounter_destroy(mqtt_client->packetTickCntr); 1026 | mqtt_codec_destroy(mqtt_client->codec_handle); 1027 | clear_mqtt_options(mqtt_client); 1028 | free(mqtt_client); 1029 | } 1030 | } 1031 | 1032 | int mqtt_client_connect(MQTT_CLIENT_HANDLE handle, XIO_HANDLE xioHandle, MQTT_CLIENT_OPTIONS* mqttOptions) 1033 | { 1034 | int result; 1035 | /*SRS_MQTT_CLIENT_07_006: [If any of the parameters handle, ioHandle, or mqttOptions are NULL then mqtt_client_connect shall return a non-zero value.]*/ 1036 | if (handle == NULL || mqttOptions == NULL || xioHandle == NULL) 1037 | { 1038 | LogError("mqtt_client_connect: NULL argument (handle = %p, mqttOptions = %p, xioHandle: %p)", handle, mqttOptions, xioHandle); 1039 | result = MU_FAILURE; 1040 | } 1041 | else 1042 | { 1043 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; 1044 | mqtt_client->xioHandle = xioHandle; 1045 | mqtt_client->packetState = UNKNOWN_TYPE; 1046 | mqtt_client->qosValue = mqttOptions->qualityOfServiceValue; 1047 | mqtt_client->keepAliveInterval = mqttOptions->keepAliveInterval; 1048 | mqtt_client->maxPingRespTime = (DEFAULT_MAX_PING_RESPONSE_TIME < mqttOptions->keepAliveInterval/2) ? DEFAULT_MAX_PING_RESPONSE_TIME : mqttOptions->keepAliveInterval/2; 1049 | if (cloneMqttOptions(mqtt_client, mqttOptions) != 0) 1050 | { 1051 | LogError("Error: Clone Mqtt Options failed"); 1052 | result = MU_FAILURE; 1053 | } 1054 | /*Codes_SRS_MQTT_CLIENT_07_008: [mqtt_client_connect shall open the XIO_HANDLE by calling into the xio_open interface.]*/ 1055 | else if (xio_open(xioHandle, onOpenComplete, mqtt_client, onBytesReceived, mqtt_client, onIoError, mqtt_client) != 0) 1056 | { 1057 | /*Codes_SRS_MQTT_CLIENT_07_007: [If any failure is encountered then mqtt_client_connect shall return a non-zero value.]*/ 1058 | LogError("Error: io_open failed"); 1059 | result = MU_FAILURE; 1060 | mqtt_client->xioHandle = NULL; 1061 | // Remove cloned options 1062 | clear_mqtt_options(mqtt_client); 1063 | } 1064 | else 1065 | { 1066 | result = 0; 1067 | } 1068 | } 1069 | return result; 1070 | } 1071 | 1072 | int mqtt_client_publish(MQTT_CLIENT_HANDLE handle, MQTT_MESSAGE_HANDLE msgHandle) 1073 | { 1074 | int result; 1075 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; 1076 | if (mqtt_client == NULL || msgHandle == NULL) 1077 | { 1078 | /*Codes_SRS_MQTT_CLIENT_07_019: [If one of the parameters handle or msgHandle is NULL then mqtt_client_publish shall return a non-zero value.]*/ 1079 | LogError("Invalid parameter specified mqtt_client: %p, msgHandle: %p", mqtt_client, msgHandle); 1080 | result = MU_FAILURE; 1081 | } 1082 | else 1083 | { 1084 | /*Codes_SRS_MQTT_CLIENT_07_021: [mqtt_client_publish shall get the message information from the MQTT_MESSAGE_HANDLE.]*/ 1085 | const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle); 1086 | if (payload == NULL) 1087 | { 1088 | /*Codes_SRS_MQTT_CLIENT_07_020: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ 1089 | LogError("Error: mqttmessage_getApplicationMsg failed"); 1090 | result = MU_FAILURE; 1091 | } 1092 | else 1093 | { 1094 | STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); 1095 | 1096 | QOS_VALUE qos = mqttmessage_getQosType(msgHandle); 1097 | bool isDuplicate = mqttmessage_getIsDuplicateMsg(msgHandle); 1098 | bool isRetained = mqttmessage_getIsRetained(msgHandle); 1099 | uint16_t packetId = mqttmessage_getPacketId(msgHandle); 1100 | const char* topicName = mqttmessage_getTopicName(msgHandle); 1101 | BUFFER_HANDLE publishPacket = mqtt_codec_publish(qos, isDuplicate, isRetained, packetId, topicName, payload->message, payload->length, trace_log); 1102 | if (publishPacket == NULL) 1103 | { 1104 | /*Codes_SRS_MQTT_CLIENT_07_020: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ 1105 | LogError("Error: mqtt_codec_publish failed"); 1106 | result = MU_FAILURE; 1107 | } 1108 | else 1109 | { 1110 | mqtt_client->packetState = PUBLISH_TYPE; 1111 | 1112 | /*Codes_SRS_MQTT_CLIENT_07_022: [On success mqtt_client_publish shall send the MQTT SUBCRIBE packet to the endpoint.]*/ 1113 | size_t size = BUFFER_length(publishPacket); 1114 | if (sendPacketItem(mqtt_client, BUFFER_u_char(publishPacket), size) != 0) 1115 | { 1116 | /*Codes_SRS_MQTT_CLIENT_07_020: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ 1117 | LogError("Error: mqtt_client_publish send failed"); 1118 | result = MU_FAILURE; 1119 | } 1120 | else 1121 | { 1122 | log_outgoing_trace(mqtt_client, trace_log); 1123 | result = 0; 1124 | } 1125 | BUFFER_delete(publishPacket); 1126 | } 1127 | if (trace_log != NULL) 1128 | { 1129 | STRING_delete(trace_log); 1130 | } 1131 | } 1132 | } 1133 | return result; 1134 | } 1135 | 1136 | int mqtt_client_subscribe(MQTT_CLIENT_HANDLE handle, uint16_t packetId, SUBSCRIBE_PAYLOAD* subscribeList, size_t count) 1137 | { 1138 | int result; 1139 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; 1140 | if (mqtt_client == NULL || subscribeList == NULL || count == 0 || packetId == 0) 1141 | { 1142 | /*Codes_SRS_MQTT_CLIENT_07_013: [If any of the parameters handle, subscribeList is NULL or count is 0 then mqtt_client_subscribe shall return a non-zero value.]*/ 1143 | LogError("Invalid parameter specified mqtt_client: %p, subscribeList: %p, count: %lu, packetId: %d", mqtt_client, subscribeList, (unsigned long)count, packetId); 1144 | result = MU_FAILURE; 1145 | } 1146 | else 1147 | { 1148 | STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); 1149 | 1150 | BUFFER_HANDLE subPacket = mqtt_codec_subscribe(packetId, subscribeList, count, trace_log); 1151 | if (subPacket == NULL) 1152 | { 1153 | /*Codes_SRS_MQTT_CLIENT_07_014: [If any failure is encountered then mqtt_client_subscribe shall return a non-zero value.]*/ 1154 | LogError("Error: mqtt_codec_subscribe failed"); 1155 | result = MU_FAILURE; 1156 | } 1157 | else 1158 | { 1159 | mqtt_client->packetState = SUBSCRIBE_TYPE; 1160 | 1161 | size_t size = BUFFER_length(subPacket); 1162 | /*Codes_SRS_MQTT_CLIENT_07_015: [On success mqtt_client_subscribe shall send the MQTT SUBCRIBE packet to the endpoint.]*/ 1163 | if (sendPacketItem(mqtt_client, BUFFER_u_char(subPacket), size) != 0) 1164 | { 1165 | /*Codes_SRS_MQTT_CLIENT_07_014: [If any failure is encountered then mqtt_client_subscribe shall return a non-zero value.]*/ 1166 | LogError("Error: mqtt_client_subscribe send failed"); 1167 | result = MU_FAILURE; 1168 | } 1169 | else 1170 | { 1171 | log_outgoing_trace(mqtt_client, trace_log); 1172 | result = 0; 1173 | } 1174 | BUFFER_delete(subPacket); 1175 | } 1176 | if (trace_log != NULL) 1177 | { 1178 | STRING_delete(trace_log); 1179 | } 1180 | } 1181 | return result; 1182 | } 1183 | 1184 | int mqtt_client_unsubscribe(MQTT_CLIENT_HANDLE handle, uint16_t packetId, const char** unsubscribeList, size_t count) 1185 | { 1186 | int result; 1187 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; 1188 | if (mqtt_client == NULL || unsubscribeList == NULL || count == 0 || packetId == 0) 1189 | { 1190 | /*Codes_SRS_MQTT_CLIENT_07_016: [If any of the parameters handle, unsubscribeList is NULL or count is 0 then mqtt_client_unsubscribe shall return a non-zero value.]*/ 1191 | LogError("Invalid parameter specified mqtt_client: %p, unsubscribeList: %p, count: %lu, packetId: %d", mqtt_client, unsubscribeList, (unsigned long)count, packetId); 1192 | result = MU_FAILURE; 1193 | } 1194 | else 1195 | { 1196 | STRING_HANDLE trace_log = construct_trace_log_handle(mqtt_client); 1197 | 1198 | BUFFER_HANDLE unsubPacket = mqtt_codec_unsubscribe(packetId, unsubscribeList, count, trace_log); 1199 | if (unsubPacket == NULL) 1200 | { 1201 | /*Codes_SRS_MQTT_CLIENT_07_017: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.]*/ 1202 | LogError("Error: mqtt_codec_unsubscribe failed"); 1203 | result = MU_FAILURE; 1204 | } 1205 | else 1206 | { 1207 | mqtt_client->packetState = UNSUBSCRIBE_TYPE; 1208 | 1209 | size_t size = BUFFER_length(unsubPacket); 1210 | /*Codes_SRS_MQTT_CLIENT_07_018: [On success mqtt_client_unsubscribe shall send the MQTT SUBCRIBE packet to the endpoint.]*/ 1211 | if (sendPacketItem(mqtt_client, BUFFER_u_char(unsubPacket), size) != 0) 1212 | { 1213 | /*Codes_SRS_MQTT_CLIENT_07_017: [If any failure is encountered then mqtt_client_unsubscribe shall return a non-zero value.].]*/ 1214 | LogError("Error: mqtt_client_unsubscribe send failed"); 1215 | result = MU_FAILURE; 1216 | } 1217 | else 1218 | { 1219 | log_outgoing_trace(mqtt_client, trace_log); 1220 | result = 0; 1221 | } 1222 | BUFFER_delete(unsubPacket); 1223 | } 1224 | if (trace_log != NULL) 1225 | { 1226 | STRING_delete(trace_log); 1227 | } 1228 | } 1229 | return result; 1230 | } 1231 | 1232 | int mqtt_client_disconnect(MQTT_CLIENT_HANDLE handle, ON_MQTT_DISCONNECTED_CALLBACK callback, void* ctx) 1233 | { 1234 | int result; 1235 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; 1236 | if (mqtt_client == NULL) 1237 | { 1238 | /*Codes_SRS_MQTT_CLIENT_07_010: [If the parameters handle is NULL then mqtt_client_disconnect shall return a non-zero value.]*/ 1239 | result = MU_FAILURE; 1240 | } 1241 | else 1242 | { 1243 | if (mqtt_client->mqtt_status & MQTT_STATUS_CLIENT_CONNECTED) 1244 | { 1245 | BUFFER_HANDLE disconnectPacket = mqtt_codec_disconnect(); 1246 | if (disconnectPacket == NULL) 1247 | { 1248 | /*Codes_SRS_MQTT_CLIENT_07_011: [If any failure is encountered then mqtt_client_disconnect shall return a non-zero value.]*/ 1249 | LogError("Error: mqtt_client_disconnect failed"); 1250 | mqtt_client->packetState = PACKET_TYPE_ERROR; 1251 | result = MU_FAILURE; 1252 | } 1253 | else 1254 | { 1255 | /* Codes_SRS_MQTT_CLIENT_07_037: [ if callback is not NULL callback shall be called once the mqtt connection has been disconnected ] */ 1256 | mqtt_client->disconnect_cb = callback; 1257 | mqtt_client->disconnect_ctx = ctx; 1258 | mqtt_client->packetState = DISCONNECT_TYPE; 1259 | 1260 | size_t size = BUFFER_length(disconnectPacket); 1261 | /*Codes_SRS_MQTT_CLIENT_07_012: [On success mqtt_client_disconnect shall send the MQTT DISCONNECT packet to the endpoint.]*/ 1262 | if (sendPacketItem(mqtt_client, BUFFER_u_char(disconnectPacket), size) != 0) 1263 | { 1264 | /*Codes_SRS_MQTT_CLIENT_07_011: [If any failure is encountered then mqtt_client_disconnect shall return a non-zero value.]*/ 1265 | LogError("Error: mqtt_client_disconnect send failed"); 1266 | result = MU_FAILURE; 1267 | } 1268 | else 1269 | { 1270 | if (is_trace_enabled(mqtt_client)) 1271 | { 1272 | STRING_HANDLE trace_log = STRING_construct("DISCONNECT"); 1273 | log_outgoing_trace(mqtt_client, trace_log); 1274 | STRING_delete(trace_log); 1275 | } 1276 | result = 0; 1277 | } 1278 | BUFFER_delete(disconnectPacket); 1279 | } 1280 | clear_mqtt_options(mqtt_client); 1281 | } 1282 | else 1283 | { 1284 | // If the client is not connected then just close the underlying socket 1285 | mqtt_client->disconnect_cb = callback; 1286 | mqtt_client->disconnect_ctx = ctx; 1287 | 1288 | close_connection(mqtt_client); 1289 | clear_mqtt_options(mqtt_client); 1290 | result = 0; 1291 | } 1292 | } 1293 | return result; 1294 | } 1295 | 1296 | void mqtt_client_dowork(MQTT_CLIENT_HANDLE handle) 1297 | { 1298 | MQTT_CLIENT* mqtt_client = (MQTT_CLIENT*)handle; 1299 | /*Codes_SRS_MQTT_CLIENT_18_001: [If the client is disconnected, mqtt_client_dowork shall do nothing.]*/ 1300 | /*Codes_SRS_MQTT_CLIENT_07_023: [If the parameter handle is NULL then mqtt_client_dowork shall do nothing.]*/ 1301 | if (mqtt_client != NULL && mqtt_client->xioHandle != NULL) 1302 | { 1303 | if (mqtt_client->mqtt_status & MQTT_STATUS_PENDING_CLOSE) 1304 | { 1305 | close_connection(mqtt_client); 1306 | // turn off pending close 1307 | mqtt_client->mqtt_status &= ~MQTT_STATUS_PENDING_CLOSE; 1308 | } 1309 | else 1310 | { 1311 | /*Codes_SRS_MQTT_CLIENT_07_024: [mqtt_client_dowork shall call the xio_dowork function to complete operations.]*/ 1312 | xio_dowork(mqtt_client->xioHandle); 1313 | 1314 | /*Codes_SRS_MQTT_CLIENT_07_025: [mqtt_client_dowork shall retrieve the the last packet send value and ...]*/ 1315 | if (mqtt_client->mqtt_status & MQTT_STATUS_SOCKET_CONNECTED && 1316 | mqtt_client->mqtt_status & MQTT_STATUS_CLIENT_CONNECTED && 1317 | mqtt_client->keepAliveInterval > 0) 1318 | { 1319 | tickcounter_ms_t current_ms; 1320 | if (tickcounter_get_current_ms(mqtt_client->packetTickCntr, ¤t_ms) != 0) 1321 | { 1322 | LogError("Error: tickcounter_get_current_ms failed"); 1323 | } 1324 | else 1325 | { 1326 | /* Codes_SRS_MQTT_CLIENT_07_035: [If the timeSincePing has expired past the maxPingRespTime then mqtt_client_dowork shall call the Error Callback function with the message MQTT_CLIENT_NO_PING_RESPONSE] */ 1327 | if (mqtt_client->timeSincePing > 0 && ((current_ms - mqtt_client->timeSincePing)/1000) > mqtt_client->maxPingRespTime) 1328 | { 1329 | // We haven't gotten a ping response in the alloted time 1330 | set_error_callback(mqtt_client, MQTT_CLIENT_NO_PING_RESPONSE); 1331 | mqtt_client->timeSincePing = 0; 1332 | mqtt_client->packetSendTimeMs = 0; 1333 | mqtt_client->packetState = UNKNOWN_TYPE; 1334 | } 1335 | else if (((current_ms - mqtt_client->packetSendTimeMs) / 1000) >= mqtt_client->keepAliveInterval) 1336 | { 1337 | /*Codes_SRS_MQTT_CLIENT_07_026: [if keepAliveInternal is > 0 and the send time is greater than the MQTT KeepAliveInterval then it shall construct an MQTT PINGREQ packet.]*/ 1338 | BUFFER_HANDLE pingPacket = mqtt_codec_ping(); 1339 | if (pingPacket != NULL) 1340 | { 1341 | size_t size = BUFFER_length(pingPacket); 1342 | (void)sendPacketItem(mqtt_client, BUFFER_u_char(pingPacket), size); 1343 | BUFFER_delete(pingPacket); 1344 | (void)tickcounter_get_current_ms(mqtt_client->packetTickCntr, &mqtt_client->timeSincePing); 1345 | 1346 | if (is_trace_enabled(mqtt_client)) 1347 | { 1348 | STRING_HANDLE trace_log = STRING_construct("PINGREQ"); 1349 | log_outgoing_trace(mqtt_client, trace_log); 1350 | STRING_delete(trace_log); 1351 | } 1352 | } 1353 | } 1354 | } 1355 | } 1356 | } 1357 | } 1358 | } 1359 | 1360 | void mqtt_client_set_trace(MQTT_CLIENT_HANDLE handle, bool traceOn, bool rawBytesOn) 1361 | { 1362 | AZURE_UNREFERENCED_PARAMETER(handle); 1363 | AZURE_UNREFERENCED_PARAMETER(traceOn); 1364 | AZURE_UNREFERENCED_PARAMETER(rawBytesOn); 1365 | #ifndef NO_LOGGING 1366 | if (handle != NULL) 1367 | { 1368 | if (traceOn) 1369 | { 1370 | handle->mqtt_flags |= MQTT_FLAGS_LOG_TRACE; 1371 | } 1372 | else 1373 | { 1374 | handle->mqtt_flags &= ~MQTT_FLAGS_LOG_TRACE; 1375 | } 1376 | #ifdef ENABLE_RAW_TRACE 1377 | if (rawBytesOn) 1378 | { 1379 | handle->mqtt_flags |= MQTT_FLAGS_RAW_TRACE; 1380 | } 1381 | else 1382 | { 1383 | handle->mqtt_flags &= ~MQTT_FLAGS_RAW_TRACE; 1384 | } 1385 | #endif 1386 | } 1387 | #endif 1388 | } 1389 | -------------------------------------------------------------------------------- /src/azure_umqtt_c/mqtt_codec.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include 5 | #include 6 | #include "azure_c_shared_utility/optimize_size.h" 7 | #include "azure_c_shared_utility/gballoc.h" 8 | #include "azure_c_shared_utility/buffer_.h" 9 | #include "azure_c_shared_utility/strings.h" 10 | #include "azure_macro_utils/macro_utils.h" 11 | #include "azure_c_shared_utility/xlogging.h" 12 | #include "azure_umqtt_c/mqtt_codec.h" 13 | #include 14 | 15 | #define PAYLOAD_OFFSET 5 16 | #define PACKET_TYPE_BYTE(p) (CONTROL_PACKET_TYPE)((uint8_t)(((uint8_t)(p)) & 0xf0)) 17 | #define FLAG_VALUE_BYTE(p) ((uint8_t)(((uint8_t)(p)) & 0xf)) 18 | 19 | #define USERNAME_FLAG 0x80 20 | #define PASSWORD_FLAG 0x40 21 | #define WILL_RETAIN_FLAG 0x20 22 | #define WILL_QOS_FLAG_ 0x18 23 | #define WILL_FLAG_FLAG 0x04 24 | #define CLEAN_SESSION_FLAG 0x02 25 | 26 | #define NEXT_128_CHUNK 0x80 27 | #define PUBLISH_DUP_FLAG 0x8 28 | #define PUBLISH_QOS_EXACTLY_ONCE 0x4 29 | #define PUBLISH_QOS_AT_LEAST_ONCE 0x2 30 | #define PUBLISH_QOS_RETAIN 0x1 31 | 32 | #define PROTOCOL_NUMBER 4 33 | #define CONN_FLAG_BYTE_OFFSET 7 34 | 35 | #define CONNECT_FIXED_HEADER_SIZE 2 36 | #define CONNECT_VARIABLE_HEADER_SIZE 10 37 | #define SUBSCRIBE_FIXED_HEADER_FLAG 0x2 38 | #define UNSUBSCRIBE_FIXED_HEADER_FLAG 0x2 39 | 40 | #define MAX_SEND_SIZE 0xFFFFFF7F // 268435455 41 | 42 | // This captures the maximum packet size for 3 digits. 43 | // If it's above this value then we bail out of the loop 44 | #define MAX_3_DIGIT_PACKET_SIZE 2097152 45 | 46 | #define CODEC_STATE_VALUES \ 47 | CODEC_STATE_FIXED_HEADER, \ 48 | CODEC_STATE_VAR_HEADER, \ 49 | CODEC_STATE_PAYLOAD 50 | 51 | static const char* const TRUE_CONST = "true"; 52 | static const char* const FALSE_CONST = "false"; 53 | 54 | MU_DEFINE_ENUM(CODEC_STATE_RESULT, CODEC_STATE_VALUES); 55 | 56 | typedef struct MQTTCODEC_INSTANCE_TAG 57 | { 58 | CONTROL_PACKET_TYPE currPacket; 59 | CODEC_STATE_RESULT codecState; 60 | size_t bufferOffset; 61 | int headerFlags; 62 | BUFFER_HANDLE headerData; 63 | ON_PACKET_COMPLETE_CALLBACK packetComplete; 64 | void* callContext; 65 | uint8_t storeRemainLen[4]; 66 | size_t remainLenIndex; 67 | } MQTTCODEC_INSTANCE; 68 | 69 | typedef struct PUBLISH_HEADER_INFO_TAG 70 | { 71 | const char* topicName; 72 | uint16_t packetId; 73 | const char* msgBuffer; 74 | QOS_VALUE qualityOfServiceValue; 75 | } PUBLISH_HEADER_INFO; 76 | 77 | static const char* retrieve_qos_value(QOS_VALUE value) 78 | { 79 | switch (value) 80 | { 81 | case DELIVER_AT_MOST_ONCE: 82 | return "DELIVER_AT_MOST_ONCE"; 83 | case DELIVER_AT_LEAST_ONCE: 84 | return "DELIVER_AT_LEAST_ONCE"; 85 | case DELIVER_EXACTLY_ONCE: 86 | default: 87 | return "DELIVER_EXACTLY_ONCE"; 88 | } 89 | } 90 | 91 | static void byteutil_writeByte(uint8_t** buffer, uint8_t value) 92 | { 93 | if (buffer != NULL) 94 | { 95 | **buffer = value; 96 | (*buffer)++; 97 | } 98 | } 99 | 100 | static void byteutil_writeInt(uint8_t** buffer, uint16_t value) 101 | { 102 | if (buffer != NULL) 103 | { 104 | **buffer = (char)(value / 256); 105 | (*buffer)++; 106 | **buffer = (char)(value % 256); 107 | (*buffer)++; 108 | } 109 | } 110 | 111 | static void byteutil_writeUTF(uint8_t** buffer, const char* stringData, uint16_t len) 112 | { 113 | if (buffer != NULL) 114 | { 115 | byteutil_writeInt(buffer, len); 116 | (void)memcpy(*buffer, stringData, len); 117 | *buffer += len; 118 | } 119 | } 120 | 121 | static CONTROL_PACKET_TYPE processControlPacketType(uint8_t pktByte, int* flags) 122 | { 123 | CONTROL_PACKET_TYPE result; 124 | result = PACKET_TYPE_BYTE(pktByte); 125 | if (flags != NULL) 126 | { 127 | *flags = FLAG_VALUE_BYTE(pktByte); 128 | } 129 | return result; 130 | } 131 | 132 | static int addListItemsToUnsubscribePacket(BUFFER_HANDLE ctrlPacket, const char** payloadList, size_t payloadCount, STRING_HANDLE trace_log) 133 | { 134 | int result = 0; 135 | size_t index = 0; 136 | for (index = 0; index < payloadCount && result == 0; index++) 137 | { 138 | // Add the Payload 139 | size_t offsetLen = BUFFER_length(ctrlPacket); 140 | size_t topicLen = strlen(payloadList[index]); 141 | if (topicLen > USHRT_MAX) 142 | { 143 | result = MU_FAILURE; 144 | } 145 | else if (BUFFER_enlarge(ctrlPacket, topicLen + 2) != 0) 146 | { 147 | result = MU_FAILURE; 148 | } 149 | else 150 | { 151 | uint8_t* iterator = BUFFER_u_char(ctrlPacket); 152 | iterator += offsetLen; 153 | byteutil_writeUTF(&iterator, payloadList[index], (uint16_t)topicLen); 154 | } 155 | if (trace_log != NULL) 156 | { 157 | STRING_sprintf(trace_log, " | TOPIC_NAME: %s", payloadList[index]); 158 | } 159 | } 160 | return result; 161 | } 162 | 163 | static int addListItemsToSubscribePacket(BUFFER_HANDLE ctrlPacket, SUBSCRIBE_PAYLOAD* payloadList, size_t payloadCount, STRING_HANDLE trace_log) 164 | { 165 | int result = 0; 166 | size_t index = 0; 167 | for (index = 0; index < payloadCount && result == 0; index++) 168 | { 169 | // Add the Payload 170 | size_t offsetLen = BUFFER_length(ctrlPacket); 171 | size_t topicLen = strlen(payloadList[index].subscribeTopic); 172 | if (topicLen > USHRT_MAX) 173 | { 174 | result = MU_FAILURE; 175 | } 176 | else if (BUFFER_enlarge(ctrlPacket, topicLen + 2 + 1) != 0) 177 | { 178 | result = MU_FAILURE; 179 | } 180 | else 181 | { 182 | uint8_t* iterator = BUFFER_u_char(ctrlPacket); 183 | iterator += offsetLen; 184 | byteutil_writeUTF(&iterator, payloadList[index].subscribeTopic, (uint16_t)topicLen); 185 | *iterator = payloadList[index].qosReturn; 186 | 187 | if (trace_log != NULL) 188 | { 189 | STRING_sprintf(trace_log, " | TOPIC_NAME: %s | QOS: %d", payloadList[index].subscribeTopic, (int)payloadList[index].qosReturn); 190 | } 191 | } 192 | } 193 | return result; 194 | } 195 | 196 | static int constructConnectVariableHeader(BUFFER_HANDLE ctrlPacket, const MQTT_CLIENT_OPTIONS* mqttOptions, STRING_HANDLE trace_log) 197 | { 198 | int result = 0; 199 | if (BUFFER_enlarge(ctrlPacket, CONNECT_VARIABLE_HEADER_SIZE) != 0) 200 | { 201 | result = MU_FAILURE; 202 | } 203 | else 204 | { 205 | uint8_t* iterator = BUFFER_u_char(ctrlPacket); 206 | if (iterator == NULL) 207 | { 208 | result = MU_FAILURE; 209 | } 210 | else 211 | { 212 | if (trace_log != NULL) 213 | { 214 | STRING_sprintf(trace_log, " | VER: %d | KEEPALIVE: %d | FLAGS:", PROTOCOL_NUMBER, mqttOptions->keepAliveInterval); 215 | } 216 | byteutil_writeUTF(&iterator, "MQTT", 4); 217 | byteutil_writeByte(&iterator, PROTOCOL_NUMBER); 218 | byteutil_writeByte(&iterator, 0); // Flags will be entered later 219 | byteutil_writeInt(&iterator, mqttOptions->keepAliveInterval); 220 | result = 0; 221 | } 222 | } 223 | return result; 224 | } 225 | 226 | static int constructPublishVariableHeader(BUFFER_HANDLE ctrlPacket, const PUBLISH_HEADER_INFO* publishHeader, STRING_HANDLE trace_log) 227 | { 228 | int result = 0; 229 | size_t topicLen = 0; 230 | size_t spaceLen = 0; 231 | size_t idLen = 0; 232 | 233 | size_t currLen = BUFFER_length(ctrlPacket); 234 | 235 | topicLen = strlen(publishHeader->topicName); 236 | spaceLen += 2; 237 | 238 | if (publishHeader->qualityOfServiceValue != DELIVER_AT_MOST_ONCE) 239 | { 240 | // Packet Id is only set if the QOS is not 0 241 | idLen = 2; 242 | } 243 | 244 | if (topicLen > USHRT_MAX) 245 | { 246 | result = MU_FAILURE; 247 | } 248 | else if (BUFFER_enlarge(ctrlPacket, topicLen + idLen + spaceLen) != 0) 249 | { 250 | result = MU_FAILURE; 251 | } 252 | else 253 | { 254 | uint8_t* iterator = BUFFER_u_char(ctrlPacket); 255 | if (iterator == NULL) 256 | { 257 | result = MU_FAILURE; 258 | } 259 | else 260 | { 261 | iterator += currLen; 262 | /* The Topic Name MUST be present as the first field in the PUBLISH Packet Variable header.It MUST be 792 a UTF-8 encoded string [MQTT-3.3.2-1] as defined in section 1.5.3.*/ 263 | byteutil_writeUTF(&iterator, publishHeader->topicName, (uint16_t)topicLen); 264 | if (trace_log != NULL) 265 | { 266 | STRING_sprintf(trace_log, " | TOPIC_NAME: %s", publishHeader->topicName); 267 | } 268 | if (idLen > 0) 269 | { 270 | if (trace_log != NULL) 271 | { 272 | STRING_sprintf(trace_log, " | PACKET_ID: %"PRIu16, publishHeader->packetId); 273 | } 274 | byteutil_writeInt(&iterator, publishHeader->packetId); 275 | } 276 | result = 0; 277 | } 278 | } 279 | return result; 280 | } 281 | 282 | static int constructSubscibeTypeVariableHeader(BUFFER_HANDLE ctrlPacket, uint16_t packetId) 283 | { 284 | int result = 0; 285 | if (BUFFER_enlarge(ctrlPacket, 2) != 0) 286 | { 287 | result = MU_FAILURE; 288 | } 289 | else 290 | { 291 | uint8_t* iterator = BUFFER_u_char(ctrlPacket); 292 | if (iterator == NULL) 293 | { 294 | result = MU_FAILURE; 295 | } 296 | else 297 | { 298 | byteutil_writeInt(&iterator, packetId); 299 | result = 0; 300 | } 301 | } 302 | return result; 303 | } 304 | 305 | static BUFFER_HANDLE constructPublishReply(CONTROL_PACKET_TYPE type, uint8_t flags, uint16_t packetId) 306 | { 307 | BUFFER_HANDLE result = BUFFER_new(); 308 | if (result != NULL) 309 | { 310 | if (BUFFER_pre_build(result, 4) != 0) 311 | { 312 | BUFFER_delete(result); 313 | result = NULL; 314 | } 315 | else 316 | { 317 | uint8_t* iterator = BUFFER_u_char(result); 318 | if (iterator == NULL) 319 | { 320 | BUFFER_delete(result); 321 | result = NULL; 322 | } 323 | else 324 | { 325 | *iterator = (uint8_t)type | flags; 326 | iterator++; 327 | *iterator = 0x2; 328 | iterator++; 329 | byteutil_writeInt(&iterator, packetId); 330 | } 331 | } 332 | } 333 | return result; 334 | } 335 | 336 | static int constructFixedHeader(BUFFER_HANDLE ctrlPacket, CONTROL_PACKET_TYPE packetType, uint8_t flags) 337 | { 338 | int result; 339 | size_t packetLen = BUFFER_length(ctrlPacket); 340 | uint8_t remainSize[4] ={ 0 }; 341 | size_t index = 0; 342 | 343 | // Calculate the length of packet 344 | do 345 | { 346 | uint8_t encode = packetLen % 128; 347 | packetLen /= 128; 348 | // if there are more data to encode, set the top bit of this byte 349 | if (packetLen > 0) 350 | { 351 | encode |= NEXT_128_CHUNK; 352 | } 353 | remainSize[index++] = encode; 354 | } while (packetLen > 0); 355 | 356 | BUFFER_HANDLE fixedHeader = BUFFER_new(); 357 | if (fixedHeader == NULL) 358 | { 359 | result = MU_FAILURE; 360 | } 361 | else if (BUFFER_pre_build(fixedHeader, index + 1) != 0) 362 | { 363 | BUFFER_delete(fixedHeader); 364 | result = MU_FAILURE; 365 | } 366 | else 367 | { 368 | uint8_t* iterator = BUFFER_u_char(fixedHeader); 369 | *iterator = (uint8_t)packetType | flags; 370 | iterator++; 371 | (void)memcpy(iterator, remainSize, index); 372 | 373 | result = BUFFER_prepend(ctrlPacket, fixedHeader); 374 | BUFFER_delete(fixedHeader); 375 | } 376 | return result; 377 | } 378 | 379 | static int constructConnPayload(BUFFER_HANDLE ctrlPacket, const MQTT_CLIENT_OPTIONS* mqttOptions, STRING_HANDLE trace_log) 380 | { 381 | int result = 0; 382 | size_t clientLen = 0; 383 | size_t usernameLen = 0; 384 | size_t passwordLen = 0; 385 | size_t willMessageLen = 0; 386 | size_t willTopicLen = 0; 387 | size_t spaceLen = 0; 388 | size_t currLen = 0; 389 | size_t totalLen = 0; 390 | 391 | if (mqttOptions->clientId != NULL) 392 | { 393 | spaceLen += 2; 394 | clientLen = strlen(mqttOptions->clientId); 395 | } 396 | if (mqttOptions->username != NULL) 397 | { 398 | spaceLen += 2; 399 | usernameLen = strlen(mqttOptions->username); 400 | } 401 | if (mqttOptions->password != NULL) 402 | { 403 | spaceLen += 2; 404 | passwordLen = strlen(mqttOptions->password); 405 | } 406 | if (mqttOptions->willMessage != NULL) 407 | { 408 | spaceLen += 2; 409 | willMessageLen = strlen(mqttOptions->willMessage); 410 | } 411 | if (mqttOptions->willTopic != NULL) 412 | { 413 | spaceLen += 2; 414 | willTopicLen = strlen(mqttOptions->willTopic); 415 | } 416 | 417 | currLen = BUFFER_length(ctrlPacket); 418 | totalLen = clientLen + usernameLen + passwordLen + willMessageLen + willTopicLen + spaceLen; 419 | 420 | // Validate the Username & Password 421 | if (clientLen > USHRT_MAX) 422 | { 423 | result = MU_FAILURE; 424 | } 425 | else if (usernameLen == 0 && passwordLen > 0) 426 | { 427 | result = MU_FAILURE; 428 | } 429 | else if ((willMessageLen > 0 && willTopicLen == 0) || (willTopicLen > 0 && willMessageLen == 0)) 430 | { 431 | result = MU_FAILURE; 432 | } 433 | else if (BUFFER_enlarge(ctrlPacket, totalLen) != 0) 434 | { 435 | result = MU_FAILURE; 436 | } 437 | else 438 | { 439 | uint8_t* packet = BUFFER_u_char(ctrlPacket); 440 | uint8_t* iterator = packet; 441 | 442 | iterator += currLen; 443 | byteutil_writeUTF(&iterator, mqttOptions->clientId, (uint16_t)clientLen); 444 | 445 | // TODO: Read on the Will Topic 446 | if (willMessageLen > USHRT_MAX || willTopicLen > USHRT_MAX || usernameLen > USHRT_MAX || passwordLen > USHRT_MAX) 447 | { 448 | result = MU_FAILURE; 449 | } 450 | else 451 | { 452 | STRING_HANDLE connect_payload_trace = NULL; 453 | if (trace_log != NULL) 454 | { 455 | connect_payload_trace = STRING_new(); 456 | } 457 | if (willMessageLen > 0 && willTopicLen > 0) 458 | { 459 | if (trace_log != NULL) 460 | { 461 | (void)STRING_sprintf(connect_payload_trace, " | WILL_TOPIC: %s", mqttOptions->willTopic); 462 | } 463 | packet[CONN_FLAG_BYTE_OFFSET] |= WILL_FLAG_FLAG; 464 | byteutil_writeUTF(&iterator, mqttOptions->willTopic, (uint16_t)willTopicLen); 465 | packet[CONN_FLAG_BYTE_OFFSET] |= (mqttOptions->qualityOfServiceValue << 3); 466 | if (mqttOptions->messageRetain) 467 | { 468 | packet[CONN_FLAG_BYTE_OFFSET] |= WILL_RETAIN_FLAG; 469 | } 470 | byteutil_writeUTF(&iterator, mqttOptions->willMessage, (uint16_t)willMessageLen); 471 | } 472 | if (usernameLen > 0) 473 | { 474 | packet[CONN_FLAG_BYTE_OFFSET] |= USERNAME_FLAG; 475 | byteutil_writeUTF(&iterator, mqttOptions->username, (uint16_t)usernameLen); 476 | if (trace_log != NULL) 477 | { 478 | (void)STRING_sprintf(connect_payload_trace, " | USERNAME: %s", mqttOptions->username); 479 | } 480 | } 481 | if (passwordLen > 0) 482 | { 483 | packet[CONN_FLAG_BYTE_OFFSET] |= PASSWORD_FLAG; 484 | byteutil_writeUTF(&iterator, mqttOptions->password, (uint16_t)passwordLen); 485 | if (trace_log != NULL) 486 | { 487 | (void)STRING_sprintf(connect_payload_trace, " | PWD: XXXX"); 488 | } 489 | } 490 | // TODO: Get the rest of the flags 491 | if (trace_log != NULL) 492 | { 493 | (void)STRING_sprintf(connect_payload_trace, " | CLEAN: %s", mqttOptions->useCleanSession ? "1" : "0"); 494 | } 495 | if (mqttOptions->useCleanSession) 496 | { 497 | packet[CONN_FLAG_BYTE_OFFSET] |= CLEAN_SESSION_FLAG; 498 | } 499 | if (trace_log != NULL) 500 | { 501 | (void)STRING_sprintf(trace_log, " %lu", packet[CONN_FLAG_BYTE_OFFSET]); 502 | (void)STRING_concat_with_STRING(trace_log, connect_payload_trace); 503 | STRING_delete(connect_payload_trace); 504 | } 505 | result = 0; 506 | } 507 | } 508 | return result; 509 | } 510 | 511 | static int prepareheaderDataInfo(MQTTCODEC_INSTANCE* codecData, uint8_t remainLen) 512 | { 513 | int result; 514 | result = 0; 515 | codecData->storeRemainLen[codecData->remainLenIndex++] = remainLen; 516 | if (remainLen <= 0x7f) 517 | { 518 | int multiplier = 1; 519 | int totalLen = 0; 520 | size_t index = 0; 521 | uint8_t encodeByte = 0; 522 | do 523 | { 524 | encodeByte = codecData->storeRemainLen[index++]; 525 | totalLen += (encodeByte & 127) * multiplier; 526 | multiplier *= NEXT_128_CHUNK; 527 | 528 | if (multiplier > MAX_3_DIGIT_PACKET_SIZE) 529 | { 530 | result = MU_FAILURE; 531 | break; 532 | } 533 | } while ((encodeByte & NEXT_128_CHUNK) != 0); 534 | 535 | if (result != 0 || totalLen > MAX_SEND_SIZE) 536 | { 537 | LogError("Receive buffer too large for MQTT packet"); 538 | result = MU_FAILURE; 539 | } 540 | else 541 | { 542 | codecData->codecState = CODEC_STATE_VAR_HEADER; 543 | 544 | // Reset remainLen Index 545 | codecData->remainLenIndex = 0; 546 | memset(codecData->storeRemainLen, 0, 4 * sizeof(uint8_t)); 547 | 548 | if (totalLen > 0) 549 | { 550 | codecData->bufferOffset = 0; 551 | codecData->headerData = BUFFER_new(); 552 | if (codecData->headerData == NULL) 553 | { 554 | /* Codes_SRS_MQTT_CODEC_07_035: [ If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value. ] */ 555 | LogError("Failed BUFFER_new"); 556 | result = MU_FAILURE; 557 | } 558 | else 559 | { 560 | if (BUFFER_pre_build(codecData->headerData, totalLen) != 0) 561 | { 562 | /* Codes_SRS_MQTT_CODEC_07_035: [ If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value. ] */ 563 | LogError("Failed BUFFER_pre_build"); 564 | result = MU_FAILURE; 565 | } 566 | 567 | } 568 | } 569 | } 570 | } 571 | return result; 572 | } 573 | 574 | static void completePacketData(MQTTCODEC_INSTANCE* codecData) 575 | { 576 | if (codecData->packetComplete != NULL) 577 | { 578 | codecData->packetComplete(codecData->callContext, codecData->currPacket, codecData->headerFlags, codecData->headerData); 579 | } 580 | 581 | // Clean up data 582 | codecData->currPacket = UNKNOWN_TYPE; 583 | codecData->codecState = CODEC_STATE_FIXED_HEADER; 584 | codecData->headerFlags = 0; 585 | BUFFER_delete(codecData->headerData); 586 | codecData->headerData = NULL; 587 | } 588 | 589 | static void clear_codec_data(MQTTCODEC_INSTANCE* codec_data) 590 | { 591 | // Clear the code instance data 592 | codec_data->currPacket = UNKNOWN_TYPE; 593 | codec_data->codecState = CODEC_STATE_FIXED_HEADER; 594 | codec_data->headerFlags = 0; 595 | codec_data->bufferOffset = 0; 596 | codec_data->headerData = NULL; 597 | memset(codec_data->storeRemainLen, 0, 4 * sizeof(uint8_t)); 598 | codec_data->remainLenIndex = 0; 599 | } 600 | 601 | void mqtt_codec_reset(MQTTCODEC_HANDLE handle) 602 | { 603 | if (handle != NULL) 604 | { 605 | clear_codec_data(handle); 606 | } 607 | } 608 | 609 | MQTTCODEC_HANDLE mqtt_codec_create(ON_PACKET_COMPLETE_CALLBACK packetComplete, void* callbackCtx) 610 | { 611 | MQTTCODEC_HANDLE result; 612 | result = malloc(sizeof(MQTTCODEC_INSTANCE)); 613 | /* Codes_SRS_MQTT_CODEC_07_001: [If a failure is encountered then mqtt_codec_create shall return NULL.] */ 614 | if (result != NULL) 615 | { 616 | /* Codes_SRS_MQTT_CODEC_07_002: [On success mqtt_codec_create shall return a MQTTCODEC_HANDLE value.] */ 617 | clear_codec_data(result); 618 | result->packetComplete = packetComplete; 619 | result->callContext = callbackCtx; 620 | } 621 | return result; 622 | } 623 | 624 | void mqtt_codec_destroy(MQTTCODEC_HANDLE handle) 625 | { 626 | /* Codes_SRS_MQTT_CODEC_07_003: [If the handle parameter is NULL then mqtt_codec_destroy shall do nothing.] */ 627 | if (handle != NULL) 628 | { 629 | MQTTCODEC_INSTANCE* codecData = (MQTTCODEC_INSTANCE*)handle; 630 | /* Codes_SRS_MQTT_CODEC_07_004: [mqtt_codec_destroy shall deallocate all memory that has been allocated by this object.] */ 631 | BUFFER_delete(codecData->headerData); 632 | free(codecData); 633 | } 634 | } 635 | 636 | BUFFER_HANDLE mqtt_codec_connect(const MQTT_CLIENT_OPTIONS* mqttOptions, STRING_HANDLE trace_log) 637 | { 638 | BUFFER_HANDLE result; 639 | /* Codes_SRS_MQTT_CODEC_07_008: [If the parameters mqttOptions is NULL then mqtt_codec_connect shall return a null value.] */ 640 | if (mqttOptions == NULL) 641 | { 642 | result = NULL; 643 | } 644 | else 645 | { 646 | /* Codes_SRS_MQTT_CODEC_07_009: [mqtt_codec_connect shall construct a BUFFER_HANDLE that represents a MQTT CONNECT packet.] */ 647 | result = BUFFER_new(); 648 | if (result != NULL) 649 | { 650 | STRING_HANDLE varible_header_log = NULL; 651 | if (trace_log != NULL) 652 | { 653 | varible_header_log = STRING_new(); 654 | } 655 | // Add Variable Header Information 656 | if (constructConnectVariableHeader(result, mqttOptions, varible_header_log) != 0) 657 | { 658 | /* Codes_SRS_MQTT_CODEC_07_010: [If any error is encountered then mqtt_codec_connect shall return NULL.] */ 659 | BUFFER_delete(result); 660 | result = NULL; 661 | } 662 | else 663 | { 664 | if (constructConnPayload(result, mqttOptions, varible_header_log) != 0) 665 | { 666 | /* Codes_SRS_MQTT_CODEC_07_010: [If any error is encountered then mqtt_codec_connect shall return NULL.] */ 667 | BUFFER_delete(result); 668 | result = NULL; 669 | } 670 | else 671 | { 672 | if (trace_log != NULL) 673 | { 674 | (void)STRING_copy(trace_log, "CONNECT"); 675 | } 676 | if (constructFixedHeader(result, CONNECT_TYPE, 0) != 0) 677 | { 678 | /* Codes_SRS_MQTT_CODEC_07_010: [If any error is encountered then mqtt_codec_connect shall return NULL.] */ 679 | BUFFER_delete(result); 680 | result = NULL; 681 | } 682 | else 683 | { 684 | if (trace_log != NULL) 685 | { 686 | (void)STRING_concat_with_STRING(trace_log, varible_header_log); 687 | } 688 | } 689 | } 690 | if (varible_header_log != NULL) 691 | { 692 | STRING_delete(varible_header_log); 693 | } 694 | } 695 | } 696 | } 697 | return result; 698 | } 699 | 700 | BUFFER_HANDLE mqtt_codec_disconnect() 701 | { 702 | /* Codes_SRS_MQTT_CODEC_07_011: [On success mqtt_codec_disconnect shall construct a BUFFER_HANDLE that represents a MQTT DISCONNECT packet.] */ 703 | BUFFER_HANDLE result = BUFFER_new(); 704 | if (result != NULL) 705 | { 706 | if (BUFFER_enlarge(result, 2) != 0) 707 | { 708 | /* Codes_SRS_MQTT_CODEC_07_012: [If any error is encountered mqtt_codec_disconnect shall return NULL.] */ 709 | BUFFER_delete(result); 710 | result = NULL; 711 | } 712 | else 713 | { 714 | uint8_t* iterator = BUFFER_u_char(result); 715 | if (iterator == NULL) 716 | { 717 | /* Codes_SRS_MQTT_CODEC_07_012: [If any error is encountered mqtt_codec_disconnect shall return NULL.] */ 718 | BUFFER_delete(result); 719 | result = NULL; 720 | } 721 | else 722 | { 723 | iterator[0] = DISCONNECT_TYPE; 724 | iterator[1] = 0; 725 | } 726 | } 727 | } 728 | return result; 729 | } 730 | 731 | BUFFER_HANDLE mqtt_codec_publish(QOS_VALUE qosValue, bool duplicateMsg, bool serverRetain, uint16_t packetId, const char* topicName, const uint8_t* msgBuffer, size_t buffLen, STRING_HANDLE trace_log) 732 | { 733 | BUFFER_HANDLE result; 734 | /* Codes_SRS_MQTT_CODEC_07_005: [If the parameters topicName is NULL then mqtt_codec_publish shall return NULL.] */ 735 | if (topicName == NULL) 736 | { 737 | result = NULL; 738 | } 739 | /* Codes_SRS_MQTT_CODEC_07_036: [mqtt_codec_publish shall return NULL if the buffLen variable is greater than the MAX_SEND_SIZE (0xFFFFFF7F).] */ 740 | else if (buffLen > MAX_SEND_SIZE) 741 | { 742 | /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ 743 | result = NULL; 744 | } 745 | else 746 | { 747 | PUBLISH_HEADER_INFO publishInfo ={ 0 }; 748 | publishInfo.topicName = topicName; 749 | publishInfo.packetId = packetId; 750 | publishInfo.qualityOfServiceValue = qosValue; 751 | 752 | uint8_t headerFlags = 0; 753 | if (duplicateMsg) headerFlags |= PUBLISH_DUP_FLAG; 754 | if (serverRetain) headerFlags |= PUBLISH_QOS_RETAIN; 755 | if (qosValue != DELIVER_AT_MOST_ONCE) 756 | { 757 | if (qosValue == DELIVER_AT_LEAST_ONCE) 758 | { 759 | headerFlags |= PUBLISH_QOS_AT_LEAST_ONCE; 760 | } 761 | else 762 | { 763 | headerFlags |= PUBLISH_QOS_EXACTLY_ONCE; 764 | } 765 | } 766 | 767 | /* Codes_SRS_MQTT_CODEC_07_007: [mqtt_codec_publish shall return a BUFFER_HANDLE that represents a MQTT PUBLISH message.] */ 768 | result = BUFFER_new(); 769 | if (result != NULL) 770 | { 771 | STRING_HANDLE varible_header_log = NULL; 772 | if (trace_log != NULL) 773 | { 774 | varible_header_log = STRING_construct_sprintf(" | IS_DUP: %s | RETAIN: %d | QOS: %s", duplicateMsg ? TRUE_CONST : FALSE_CONST, 775 | serverRetain ? 1 : 0, 776 | retrieve_qos_value(publishInfo.qualityOfServiceValue) ); 777 | } 778 | 779 | if (constructPublishVariableHeader(result, &publishInfo, varible_header_log) != 0) 780 | { 781 | /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ 782 | BUFFER_delete(result); 783 | result = NULL; 784 | } 785 | else 786 | { 787 | size_t payloadOffset = BUFFER_length(result); 788 | if (buffLen > 0) 789 | { 790 | if (BUFFER_enlarge(result, buffLen) != 0) 791 | { 792 | /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ 793 | BUFFER_delete(result); 794 | result = NULL; 795 | } 796 | else 797 | { 798 | uint8_t* iterator = BUFFER_u_char(result); 799 | if (iterator == NULL) 800 | { 801 | /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ 802 | BUFFER_delete(result); 803 | result = NULL; 804 | } 805 | else 806 | { 807 | iterator += payloadOffset; 808 | // Write Message 809 | (void)memcpy(iterator, msgBuffer, buffLen); 810 | if (trace_log) 811 | { 812 | STRING_sprintf(varible_header_log, " | PAYLOAD_LEN: %lu", (unsigned long)buffLen); 813 | } 814 | } 815 | } 816 | } 817 | 818 | if (result != NULL) 819 | { 820 | if (trace_log != NULL) 821 | { 822 | (void)STRING_copy(trace_log, "PUBLISH"); 823 | } 824 | if (constructFixedHeader(result, PUBLISH_TYPE, headerFlags) != 0) 825 | { 826 | /* Codes_SRS_MQTT_CODEC_07_006: [If any error is encountered then mqtt_codec_publish shall return NULL.] */ 827 | BUFFER_delete(result); 828 | result = NULL; 829 | } 830 | else 831 | { 832 | if (trace_log != NULL) 833 | { 834 | (void)STRING_concat_with_STRING(trace_log, varible_header_log); 835 | } 836 | } 837 | } 838 | } 839 | if (varible_header_log != NULL) 840 | { 841 | STRING_delete(varible_header_log); 842 | } 843 | } 844 | } 845 | return result; 846 | } 847 | 848 | BUFFER_HANDLE mqtt_codec_publishAck(uint16_t packetId) 849 | { 850 | /* Codes_SRS_MQTT_CODEC_07_013: [On success mqtt_codec_publishAck shall return a BUFFER_HANDLE representation of a MQTT PUBACK packet.] */ 851 | /* Codes_SRS_MQTT_CODEC_07_014 : [If any error is encountered then mqtt_codec_publishAck shall return NULL.] */ 852 | BUFFER_HANDLE result = constructPublishReply(PUBACK_TYPE, 0, packetId); 853 | return result; 854 | } 855 | 856 | BUFFER_HANDLE mqtt_codec_publishReceived(uint16_t packetId) 857 | { 858 | /* Codes_SRS_MQTT_CODEC_07_015: [On success mqtt_codec_publishRecieved shall return a BUFFER_HANDLE representation of a MQTT PUBREC packet.] */ 859 | /* Codes_SRS_MQTT_CODEC_07_016 : [If any error is encountered then mqtt_codec_publishRecieved shall return NULL.] */ 860 | BUFFER_HANDLE result = constructPublishReply(PUBREC_TYPE, 0, packetId); 861 | return result; 862 | } 863 | 864 | BUFFER_HANDLE mqtt_codec_publishRelease(uint16_t packetId) 865 | { 866 | /* Codes_SRS_MQTT_CODEC_07_017: [On success mqtt_codec_publishRelease shall return a BUFFER_HANDLE representation of a MQTT PUBREL packet.] */ 867 | /* Codes_SRS_MQTT_CODEC_07_018 : [If any error is encountered then mqtt_codec_publishRelease shall return NULL.] */ 868 | BUFFER_HANDLE result = constructPublishReply(PUBREL_TYPE, 2, packetId); 869 | return result; 870 | } 871 | 872 | BUFFER_HANDLE mqtt_codec_publishComplete(uint16_t packetId) 873 | { 874 | /* Codes_SRS_MQTT_CODEC_07_019: [On success mqtt_codec_publishComplete shall return a BUFFER_HANDLE representation of a MQTT PUBCOMP packet.] */ 875 | /* Codes_SRS_MQTT_CODEC_07_020 : [If any error is encountered then mqtt_codec_publishComplete shall return NULL.] */ 876 | BUFFER_HANDLE result = constructPublishReply(PUBCOMP_TYPE, 0, packetId); 877 | return result; 878 | } 879 | 880 | BUFFER_HANDLE mqtt_codec_ping() 881 | { 882 | /* Codes_SRS_MQTT_CODEC_07_021: [On success mqtt_codec_ping shall construct a BUFFER_HANDLE that represents a MQTT PINGREQ packet.] */ 883 | BUFFER_HANDLE result = BUFFER_new(); 884 | if (result != NULL) 885 | { 886 | if (BUFFER_enlarge(result, 2) != 0) 887 | { 888 | /* Codes_SRS_MQTT_CODEC_07_022: [If any error is encountered mqtt_codec_ping shall return NULL.] */ 889 | BUFFER_delete(result); 890 | result = NULL; 891 | } 892 | else 893 | { 894 | uint8_t* iterator = BUFFER_u_char(result); 895 | if (iterator == NULL) 896 | { 897 | /* Codes_SRS_MQTT_CODEC_07_022: [If any error is encountered mqtt_codec_ping shall return NULL.] */ 898 | BUFFER_delete(result); 899 | result = NULL; 900 | } 901 | else 902 | { 903 | iterator[0] = PINGREQ_TYPE; 904 | iterator[1] = 0; 905 | } 906 | } 907 | } 908 | return result; 909 | } 910 | 911 | BUFFER_HANDLE mqtt_codec_subscribe(uint16_t packetId, SUBSCRIBE_PAYLOAD* subscribeList, size_t count, STRING_HANDLE trace_log) 912 | { 913 | BUFFER_HANDLE result; 914 | /* Codes_SRS_MQTT_CODEC_07_023: [If the parameters subscribeList is NULL or if count is 0 then mqtt_codec_subscribe shall return NULL.] */ 915 | if (subscribeList == NULL || count == 0) 916 | { 917 | result = NULL; 918 | } 919 | else 920 | { 921 | /* Codes_SRS_MQTT_CODEC_07_026: [mqtt_codec_subscribe shall return a BUFFER_HANDLE that represents a MQTT SUBSCRIBE message.]*/ 922 | result = BUFFER_new(); 923 | if (result != NULL) 924 | { 925 | if (constructSubscibeTypeVariableHeader(result, packetId) != 0) 926 | { 927 | /* Codes_SRS_MQTT_CODEC_07_025: [If any error is encountered then mqtt_codec_subscribe shall return NULL.] */ 928 | BUFFER_delete(result); 929 | result = NULL; 930 | } 931 | else 932 | { 933 | STRING_HANDLE sub_trace = NULL; 934 | if (trace_log != NULL) 935 | { 936 | sub_trace = STRING_construct_sprintf(" | PACKET_ID: %"PRIu16, packetId); 937 | } 938 | /* Codes_SRS_MQTT_CODEC_07_024: [mqtt_codec_subscribe shall iterate through count items in the subscribeList.] */ 939 | if (addListItemsToSubscribePacket(result, subscribeList, count, sub_trace) != 0) 940 | { 941 | /* Codes_SRS_MQTT_CODEC_07_025: [If any error is encountered then mqtt_codec_subscribe shall return NULL.] */ 942 | BUFFER_delete(result); 943 | result = NULL; 944 | } 945 | else 946 | { 947 | 948 | if (trace_log != NULL) 949 | { 950 | STRING_concat(trace_log, "SUBSCRIBE"); 951 | } 952 | if (constructFixedHeader(result, SUBSCRIBE_TYPE, SUBSCRIBE_FIXED_HEADER_FLAG) != 0) 953 | { 954 | /* Codes_SRS_MQTT_CODEC_07_025: [If any error is encountered then mqtt_codec_subscribe shall return NULL.] */ 955 | BUFFER_delete(result); 956 | result = NULL; 957 | } 958 | else 959 | { 960 | if (trace_log != NULL) 961 | { 962 | (void)STRING_concat_with_STRING(trace_log, sub_trace); 963 | } 964 | } 965 | } 966 | if (sub_trace != NULL) 967 | { 968 | STRING_delete(sub_trace); 969 | } 970 | } 971 | } 972 | } 973 | return result; 974 | } 975 | 976 | BUFFER_HANDLE mqtt_codec_unsubscribe(uint16_t packetId, const char** unsubscribeList, size_t count, STRING_HANDLE trace_log) 977 | { 978 | BUFFER_HANDLE result; 979 | /* Codes_SRS_MQTT_CODEC_07_027: [If the parameters unsubscribeList is NULL or if count is 0 then mqtt_codec_unsubscribe shall return NULL.] */ 980 | if (unsubscribeList == NULL || count == 0) 981 | { 982 | result = NULL; 983 | } 984 | else 985 | { 986 | /* Codes_SRS_MQTT_CODEC_07_030: [mqtt_codec_unsubscribe shall return a BUFFER_HANDLE that represents a MQTT SUBSCRIBE message.] */ 987 | result = BUFFER_new(); 988 | if (result != NULL) 989 | { 990 | if (constructSubscibeTypeVariableHeader(result, packetId) != 0) 991 | { 992 | /* Codes_SRS_MQTT_CODEC_07_029: [If any error is encountered then mqtt_codec_unsubscribe shall return NULL.] */ 993 | BUFFER_delete(result); 994 | result = NULL; 995 | } 996 | else 997 | { 998 | STRING_HANDLE unsub_trace = NULL; 999 | if (trace_log != NULL) 1000 | { 1001 | unsub_trace = STRING_construct_sprintf(" | PACKET_ID: %"PRIu16, packetId); 1002 | } 1003 | /* Codes_SRS_MQTT_CODEC_07_028: [mqtt_codec_unsubscribe shall iterate through count items in the unsubscribeList.] */ 1004 | if (addListItemsToUnsubscribePacket(result, unsubscribeList, count, unsub_trace) != 0) 1005 | { 1006 | /* Codes_SRS_MQTT_CODEC_07_029: [If any error is encountered then mqtt_codec_unsubscribe shall return NULL.] */ 1007 | BUFFER_delete(result); 1008 | result = NULL; 1009 | } 1010 | else 1011 | { 1012 | if (trace_log != NULL) 1013 | { 1014 | (void)STRING_copy(trace_log, "UNSUBSCRIBE"); 1015 | } 1016 | if (constructFixedHeader(result, UNSUBSCRIBE_TYPE, UNSUBSCRIBE_FIXED_HEADER_FLAG) != 0) 1017 | { 1018 | /* Codes_SRS_MQTT_CODEC_07_029: [If any error is encountered then mqtt_codec_unsubscribe shall return NULL.] */ 1019 | BUFFER_delete(result); 1020 | result = NULL; 1021 | } 1022 | else 1023 | { 1024 | if (trace_log != NULL) 1025 | { 1026 | (void)STRING_concat_with_STRING(trace_log, unsub_trace); 1027 | } 1028 | } 1029 | } 1030 | if (unsub_trace != NULL) 1031 | { 1032 | STRING_delete(unsub_trace); 1033 | } 1034 | } 1035 | } 1036 | } 1037 | return result; 1038 | } 1039 | 1040 | int mqtt_codec_bytesReceived(MQTTCODEC_HANDLE handle, const unsigned char* buffer, size_t size) 1041 | { 1042 | int result; 1043 | MQTTCODEC_INSTANCE* codec_Data = (MQTTCODEC_INSTANCE*)handle; 1044 | /* Codes_SRS_MQTT_CODEC_07_031: [If the parameters handle or buffer is NULL then mqtt_codec_bytesReceived shall return a non-zero value.] */ 1045 | if (codec_Data == NULL) 1046 | { 1047 | result = MU_FAILURE; 1048 | } 1049 | /* Codes_SRS_MQTT_CODEC_07_031: [If the parameters handle or buffer is NULL then mqtt_codec_bytesReceived shall return a non-zero value.] */ 1050 | /* Codes_SRS_MQTT_CODEC_07_032: [If the parameters size is zero then mqtt_codec_bytesReceived shall return a non-zero value.] */ 1051 | else if (buffer == NULL || size == 0) 1052 | { 1053 | codec_Data->currPacket = PACKET_TYPE_ERROR; 1054 | result = MU_FAILURE; 1055 | } 1056 | else 1057 | { 1058 | /* Codes_SRS_MQTT_CODEC_07_033: [mqtt_codec_bytesReceived constructs a sequence of bytes into the corresponding MQTT packets and on success returns zero.] */ 1059 | result = 0; 1060 | size_t index = 0; 1061 | for (index = 0; index < size && result == 0; index++) 1062 | { 1063 | uint8_t iterator = ((int8_t*)buffer)[index]; 1064 | if (codec_Data->codecState == CODEC_STATE_FIXED_HEADER) 1065 | { 1066 | if (codec_Data->currPacket == UNKNOWN_TYPE) 1067 | { 1068 | codec_Data->currPacket = processControlPacketType(iterator, &codec_Data->headerFlags); 1069 | } 1070 | else 1071 | { 1072 | if (prepareheaderDataInfo(codec_Data, iterator) != 0) 1073 | { 1074 | /* Codes_SRS_MQTT_CODEC_07_035: [If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value.] */ 1075 | codec_Data->currPacket = PACKET_TYPE_ERROR; 1076 | result = MU_FAILURE; 1077 | } 1078 | else if (codec_Data->currPacket == PINGRESP_TYPE) 1079 | { 1080 | /* Codes_SRS_MQTT_CODEC_07_034: [Upon a constructing a complete MQTT packet mqtt_codec_bytesReceived shall call the ON_PACKET_COMPLETE_CALLBACK function.] */ 1081 | completePacketData(codec_Data); 1082 | } 1083 | } 1084 | } 1085 | else if (codec_Data->codecState == CODEC_STATE_VAR_HEADER) 1086 | { 1087 | if (codec_Data->headerData == NULL) 1088 | { 1089 | codec_Data->codecState = CODEC_STATE_PAYLOAD; 1090 | } 1091 | else 1092 | { 1093 | uint8_t* dataBytes = BUFFER_u_char(codec_Data->headerData); 1094 | if (dataBytes == NULL) 1095 | { 1096 | /* Codes_SRS_MQTT_CODEC_07_035: [If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value.] */ 1097 | codec_Data->currPacket = PACKET_TYPE_ERROR; 1098 | result = MU_FAILURE; 1099 | } 1100 | else 1101 | { 1102 | // Increment the data 1103 | dataBytes += codec_Data->bufferOffset++; 1104 | *dataBytes = iterator; 1105 | 1106 | size_t totalLen = BUFFER_length(codec_Data->headerData); 1107 | if (codec_Data->bufferOffset >= totalLen) 1108 | { 1109 | /* Codes_SRS_MQTT_CODEC_07_034: [Upon a constructing a complete MQTT packet mqtt_codec_bytesReceived shall call the ON_PACKET_COMPLETE_CALLBACK function.] */ 1110 | completePacketData(codec_Data); 1111 | } 1112 | } 1113 | } 1114 | } 1115 | else 1116 | { 1117 | /* Codes_SRS_MQTT_CODEC_07_035: [If any error is encountered then the packet state will be marked as error and mqtt_codec_bytesReceived shall return a non-zero value.] */ 1118 | codec_Data->currPacket = PACKET_TYPE_ERROR; 1119 | result = MU_FAILURE; 1120 | } 1121 | } 1122 | } 1123 | return result; 1124 | } 1125 | -------------------------------------------------------------------------------- /src/azure_umqtt_c/mqtt_message.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include 5 | #include "azure_umqtt_c/mqtt_message.h" 6 | #include "azure_c_shared_utility/optimize_size.h" 7 | #include "azure_c_shared_utility/gballoc.h" 8 | #include "azure_c_shared_utility/xlogging.h" 9 | #include "azure_c_shared_utility/string_token.h" 10 | #include "azure_macro_utils/macro_utils.h" 11 | 12 | typedef struct MQTT_MESSAGE_TAG 13 | { 14 | uint16_t packetId; 15 | QOS_VALUE qosInfo; 16 | 17 | char* topicName; 18 | APP_PAYLOAD appPayload; 19 | 20 | const char* const_topic_name; 21 | APP_PAYLOAD const_payload; 22 | 23 | bool isDuplicateMsg; 24 | bool isMessageRetained; 25 | } MQTT_MESSAGE; 26 | 27 | static MQTT_MESSAGE* create_msg_object(uint16_t packetId, QOS_VALUE qosValue) 28 | { 29 | MQTT_MESSAGE* result; 30 | result = (MQTT_MESSAGE*)malloc(sizeof(MQTT_MESSAGE)); 31 | if (result != NULL) 32 | { 33 | memset(result, 0, sizeof(MQTT_MESSAGE)); 34 | result->packetId = packetId; 35 | result->isDuplicateMsg = false; 36 | result->isMessageRetained = false; 37 | result->qosInfo = qosValue; 38 | } 39 | else 40 | { 41 | LogError("Failure unable to allocate MQTT Message."); 42 | } 43 | return result; 44 | } 45 | 46 | MQTT_MESSAGE_HANDLE mqttmessage_create_in_place(uint16_t packetId, const char* topicName, QOS_VALUE qosValue, const uint8_t* appMsg, size_t appMsgLength) 47 | { 48 | /* Codes_SRS_MQTTMESSAGE_07_026: [If the parameters topicName is NULL then mqttmessage_create_in_place shall return NULL.].] */ 49 | MQTT_MESSAGE* result; 50 | if (topicName == NULL) 51 | { 52 | LogError("Invalid Parameter topicName: %p, packetId: %d.", topicName, packetId); 53 | result = NULL; 54 | } 55 | else 56 | { 57 | result = create_msg_object(packetId, qosValue); 58 | if (result == NULL) 59 | { 60 | /* Codes_SRS_MQTTMESSAGE_07_028: [If any memory allocation fails mqttmessage_create_in_place shall free any allocated memory and return NULL.] */ 61 | LogError("Failure creating message object"); 62 | } 63 | else 64 | { 65 | /* Codes_SRS_MQTTMESSAGE_07_027: [mqttmessage_create_in_place shall use the a pointer to topicName or appMsg .] */ 66 | result->const_topic_name = topicName; 67 | result->const_payload.length = appMsgLength; 68 | if (result->const_payload.length > 0) 69 | { 70 | result->const_payload.message = (uint8_t*)appMsg; 71 | } 72 | } 73 | } 74 | /* Codes_SRS_MQTTMESSAGE_07_029: [ Upon success, mqttmessage_create_in_place shall return a NON-NULL MQTT_MESSAGE_HANDLE value.] */ 75 | return (MQTT_MESSAGE_HANDLE)result; 76 | } 77 | 78 | MQTT_MESSAGE_HANDLE mqttmessage_create(uint16_t packetId, const char* topicName, QOS_VALUE qosValue, const uint8_t* appMsg, size_t appMsgLength) 79 | { 80 | /* Codes_SRS_MQTTMESSAGE_07_001:[If the parameters topicName is NULL is zero then mqttmessage_create shall return NULL.] */ 81 | MQTT_MESSAGE* result; 82 | if (topicName == NULL) 83 | { 84 | LogError("Invalid Parameter topicName: %p, packetId: %d.", topicName, packetId); 85 | result = NULL; 86 | } 87 | else 88 | { 89 | /* Codes_SRS_MQTTMESSAGE_07_002: [mqttmessage_create shall allocate and copy the topicName and appMsg parameters.] */ 90 | result = create_msg_object(packetId, qosValue); 91 | if (result == NULL) 92 | { 93 | /* Codes_SRS_MQTTMESSAGE_07_028: [If any memory allocation fails mqttmessage_create_in_place shall free any allocated memory and return NULL.] */ 94 | LogError("Failure creating message object"); 95 | } 96 | else 97 | { 98 | if (mallocAndStrcpy_s(&result->topicName, topicName) != 0) 99 | { 100 | /* Codes_SRS_MQTTMESSAGE_07_003: [If any memory allocation fails mqttmessage_create shall free any allocated memory and return NULL.] */ 101 | LogError("Failure allocating topic name"); 102 | free(result); 103 | result = NULL; 104 | } 105 | else 106 | { 107 | /* Codes_SRS_MQTTMESSAGE_07_002: [mqttmessage_create shall allocate and copy the topicName and appMsg parameters.] */ 108 | result->appPayload.length = appMsgLength; 109 | if (result->appPayload.length > 0) 110 | { 111 | result->appPayload.message = malloc(appMsgLength); 112 | if (result->appPayload.message == NULL) 113 | { 114 | /* Codes_SRS_MQTTMESSAGE_07_003: [If any memory allocation fails mqttmessage_create shall free any allocated memory and return NULL.] */ 115 | LogError("Failure allocating message value of %lu", (unsigned long)appMsgLength); 116 | free(result->topicName); 117 | free(result); 118 | result = NULL; 119 | } 120 | else 121 | { 122 | (void)memcpy(result->appPayload.message, appMsg, appMsgLength); 123 | } 124 | } 125 | else 126 | { 127 | result->appPayload.message = NULL; 128 | } 129 | } 130 | } 131 | } 132 | /* Codes_SRS_MQTTMESSAGE_07_004: [If mqttmessage_createMessage succeeds the it shall return a NON-NULL MQTT_MESSAGE_HANDLE value.] */ 133 | return (MQTT_MESSAGE_HANDLE)result; 134 | } 135 | 136 | void mqttmessage_destroy(MQTT_MESSAGE_HANDLE handle) 137 | { 138 | /* Codes_SRS_MQTTMESSAGE_07_005: [If the handle parameter is NULL then mqttmessage_destroyMessage shall do nothing] */ 139 | if (handle != NULL) 140 | { 141 | /* Codes_SRS_MQTTMESSAGE_07_006: [mqttmessage_destroyMessage shall free all resources associated with the MQTT_MESSAGE_HANDLE value] */ 142 | if (handle->topicName != NULL) 143 | { 144 | free(handle->topicName); 145 | } 146 | if (handle->appPayload.message != NULL) 147 | { 148 | free(handle->appPayload.message); 149 | } 150 | free(handle); 151 | } 152 | } 153 | 154 | MQTT_MESSAGE_HANDLE mqttmessage_clone(MQTT_MESSAGE_HANDLE handle) 155 | { 156 | MQTT_MESSAGE_HANDLE result; 157 | if (handle == NULL) 158 | { 159 | /* Codes_SRS_MQTTMESSAGE_07_007: [If handle parameter is NULL then mqttmessage_clone shall return NULL.] */ 160 | LogError("Invalid Parameter handle: %p.", handle); 161 | result = NULL; 162 | } 163 | else 164 | { 165 | /* Codes_SRS_MQTTMESSAGE_07_008: [mqttmessage_clone shall create a new MQTT_MESSAGE_HANDLE with data content identical of the handle value.] */ 166 | result = mqttmessage_create(handle->packetId, handle->topicName, handle->qosInfo, handle->appPayload.message, handle->appPayload.length); 167 | if (result != NULL) 168 | { 169 | result->isDuplicateMsg = handle->isDuplicateMsg; 170 | result->isMessageRetained = handle->isMessageRetained; 171 | } 172 | } 173 | return result; 174 | } 175 | 176 | uint16_t mqttmessage_getPacketId(MQTT_MESSAGE_HANDLE handle) 177 | { 178 | uint16_t result; 179 | if (handle == NULL) 180 | { 181 | /* Codes_SRS_MQTTMESSAGE_07_010: [If handle is NULL then mqttmessage_getPacketId shall return 0.] */ 182 | LogError("Invalid Parameter handle: %p.", handle); 183 | result = 0; 184 | } 185 | else 186 | { 187 | /* Codes_SRS_MQTTMESSAGE_07_011: [mqttmessage_getPacketId shall return the packetId value contained in MQTT_MESSAGE_HANDLE handle.] */ 188 | result = handle->packetId; 189 | } 190 | return result; 191 | } 192 | 193 | const char* mqttmessage_getTopicName(MQTT_MESSAGE_HANDLE handle) 194 | { 195 | const char* result; 196 | if (handle == NULL) 197 | { 198 | /* Codes_SRS_MQTTMESSAGE_07_012: [If handle is NULL then mqttmessage_getTopicName shall return a NULL string.] */ 199 | LogError("Invalid Parameter handle: %p.", handle); 200 | result = NULL; 201 | } 202 | else 203 | { 204 | /* Codes_SRS_MQTTMESSAGE_07_013: [mqttmessage_getTopicName shall return the topicName contained in MQTT_MESSAGE_HANDLE handle.] */ 205 | if (handle->topicName == NULL) 206 | { 207 | result = handle->const_topic_name; 208 | } 209 | else 210 | { 211 | result = handle->topicName; 212 | } 213 | } 214 | return result; 215 | } 216 | 217 | int mqttmessage_getTopicLevels(MQTT_MESSAGE_HANDLE handle, char*** levels, size_t* count) 218 | { 219 | int result; 220 | 221 | // Codes_SRS_MQTTMESSAGE_09_001: [ If `handle`, `levels` or `count` are NULL the function shall return a non-zero value. ] 222 | if (handle == NULL || levels == NULL || count == NULL) 223 | { 224 | LogError("Invalid Parameter handle: %p, levels: %p, count: %p", handle, levels, count); 225 | result = MU_FAILURE; 226 | } 227 | else 228 | { 229 | MQTT_MESSAGE* msgInfo = (MQTT_MESSAGE*)handle; 230 | 231 | const char* topic_name = msgInfo->topicName != NULL ? msgInfo->topicName : msgInfo->const_topic_name; 232 | 233 | if (topic_name == NULL) 234 | { 235 | LogError("Topic name is NULL"); 236 | result = MU_FAILURE; 237 | } 238 | else 239 | { 240 | const char* delimiters[1]; 241 | 242 | delimiters[0] = "/"; 243 | 244 | // Codes_SRS_MQTTMESSAGE_09_002: [ The topic name, excluding the property bag, shall be split into individual tokens using "/" as separator ] 245 | // Codes_SRS_MQTTMESSAGE_09_004: [ The split tokens shall be stored in `levels` and its count in `count` ] 246 | if (StringToken_Split(topic_name, strlen(topic_name), delimiters, 1, false, levels, count) != 0) 247 | { 248 | // Codes_SRS_MQTTMESSAGE_09_003: [ If splitting fails the function shall return a non-zero value. ] 249 | LogError("Failed splitting topic levels"); 250 | result = MU_FAILURE; 251 | } 252 | else 253 | { 254 | // Codes_SRS_MQTTMESSAGE_09_005: [ If no failures occur the function shall return zero. ] 255 | result = 0; 256 | } 257 | } 258 | } 259 | 260 | return result; 261 | } 262 | 263 | QOS_VALUE mqttmessage_getQosType(MQTT_MESSAGE_HANDLE handle) 264 | { 265 | QOS_VALUE result; 266 | if (handle == NULL) 267 | { 268 | /* Codes_SRS_MQTTMESSAGE_07_014: [If handle is NULL then mqttmessage_getQosType shall return the default DELIVER_AT_MOST_ONCE value.] */ 269 | LogError("Invalid Parameter handle: %p.", handle); 270 | result = DELIVER_AT_MOST_ONCE; 271 | } 272 | else 273 | { 274 | /* Codes_SRS_MQTTMESSAGE_07_015: [mqttmessage_getQosType shall return the QOS Type value contained in MQTT_MESSAGE_HANDLE handle.] */ 275 | result = handle->qosInfo; 276 | } 277 | return result; 278 | } 279 | 280 | bool mqttmessage_getIsDuplicateMsg(MQTT_MESSAGE_HANDLE handle) 281 | { 282 | bool result; 283 | if (handle == NULL) 284 | { 285 | /* Codes_SRS_MQTTMESSAGE_07_016: [If handle is NULL then mqttmessage_getIsDuplicateMsg shall return false.] */ 286 | LogError("Invalid Parameter handle: %p.", handle); 287 | result = false; 288 | } 289 | else 290 | { 291 | /* Codes_SRS_MQTTMESSAGE_07_017: [mqttmessage_getIsDuplicateMsg shall return the isDuplicateMsg value contained in MQTT_MESSAGE_HANDLE handle.] */ 292 | result = handle->isDuplicateMsg; 293 | } 294 | return result; 295 | } 296 | 297 | bool mqttmessage_getIsRetained(MQTT_MESSAGE_HANDLE handle) 298 | { 299 | bool result; 300 | if (handle == NULL) 301 | { 302 | /* Codes_SRS_MQTTMESSAGE_07_018: [If handle is NULL then mqttmessage_getIsRetained shall return false.] */ 303 | LogError("Invalid Parameter handle: %p.", handle); 304 | result = false; 305 | } 306 | else 307 | { 308 | /* Codes_SRS_MQTTMESSAGE_07_019: [mqttmessage_getIsRetained shall return the isRetained value contained in MQTT_MESSAGE_HANDLE handle.] */ 309 | result = handle->isMessageRetained; 310 | } 311 | return result; 312 | } 313 | 314 | int mqttmessage_setIsDuplicateMsg(MQTT_MESSAGE_HANDLE handle, bool duplicateMsg) 315 | { 316 | int result; 317 | /* Codes_SRS_MQTTMESSAGE_07_022: [If handle is NULL then mqttmessage_setIsDuplicateMsg shall return a non-zero value.] */ 318 | if (handle == NULL) 319 | { 320 | LogError("Invalid Parameter handle: %p.", handle); 321 | result = MU_FAILURE; 322 | } 323 | else 324 | { 325 | /* Codes_SRS_MQTTMESSAGE_07_023: [mqttmessage_setIsDuplicateMsg shall store the duplicateMsg value in the MQTT_MESSAGE_HANDLE handle.] */ 326 | handle->isDuplicateMsg = duplicateMsg; 327 | result = 0; 328 | } 329 | return result; 330 | } 331 | 332 | int mqttmessage_setIsRetained(MQTT_MESSAGE_HANDLE handle, bool retainMsg) 333 | { 334 | int result; 335 | /* Codes_SRS_MQTTMESSAGE_07_024: [If handle is NULL then mqttmessage_setIsRetained shall return a non-zero value.] */ 336 | if (handle == NULL) 337 | { 338 | LogError("Invalid Parameter handle: %p.", handle); 339 | result = MU_FAILURE; 340 | } 341 | else 342 | { 343 | /* Codes_SRS_MQTTMESSAGE_07_025: [mqttmessage_setIsRetained shall store the retainMsg value in the MQTT_MESSAGE_HANDLE handle.] */ 344 | handle->isMessageRetained = retainMsg; 345 | result = 0; 346 | } 347 | return result; 348 | } 349 | 350 | const APP_PAYLOAD* mqttmessage_getApplicationMsg(MQTT_MESSAGE_HANDLE handle) 351 | { 352 | const APP_PAYLOAD* result; 353 | if (handle == NULL) 354 | { 355 | /* Codes_SRS_MQTTMESSAGE_07_020: [If handle is NULL or if msgLen is 0 then mqttmessage_getApplicationMsg shall return NULL.] */ 356 | LogError("Invalid Parameter handle: %p.", handle); 357 | result = NULL; 358 | } 359 | else 360 | { 361 | /* Codes_SRS_MQTTMESSAGE_07_021: [mqttmessage_getApplicationMsg shall return the applicationMsg value contained in MQTT_MESSAGE_HANDLE handle and the length of the appMsg in the msgLen parameter.] */ 362 | if (handle->const_payload.length > 0) 363 | { 364 | result = &handle->const_payload; 365 | } 366 | else 367 | { 368 | result = &handle->appPayload; 369 | } 370 | } 371 | return result; 372 | } 373 | --------------------------------------------------------------------------------