summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiwon Park <jiwony.park@samsung.com>2018-08-09 15:04:04 +0900
committerJiwon Park <jiwony.park@samsung.com>2018-09-10 14:53:54 +0900
commit4212cb115cb2c00f65a1105821c624232d3fd4ad (patch)
treec6aa8fad275a10e93a59185f41089eed51737126
parentb693c4917411911738746f91fc126631c4146591 (diff)
downloadtizen-things-mqtt-master.tar.gz
tizen-things-mqtt-master.tar.bz2
tizen-things-mqtt-master.zip
Initial commitHEADmaster
Change-Id: I8182e58be7347d65a332827c3883816eefc77d8f
-rw-r--r--.gitignore59
-rw-r--r--README.md54
-rw-r--r--publisher.js57
-rw-r--r--tts-mqtt-test/.cproject373
-rw-r--r--tts-mqtt-test/.exportMap5
-rw-r--r--tts-mqtt-test/.gitignore6
-rw-r--r--tts-mqtt-test/.project46
-rw-r--r--tts-mqtt-test/.tproject12
-rw-r--r--tts-mqtt-test/inc/log.h45
-rw-r--r--tts-mqtt-test/project_def.prop11
-rw-r--r--tts-mqtt-test/shared/res/tts-mqtt-test.pngbin0 -> 57662 bytes
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Clients.c55
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Clients.h209
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Heap.c493
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Heap.h82
-rw-r--r--tts-mqtt-test/src/paho-mqtt/LinkedList.c506
-rw-r--r--tts-mqtt-test/src/paho-mqtt/LinkedList.h105
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Log.c572
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Log.h85
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTAsync.c3331
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTAsync.h1751
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTClient.c2146
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTClient.h1414
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTClientPersistence.h254
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPacket.c737
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPacket.h260
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.c269
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.h34
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPersistence.c655
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPersistence.h74
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.c852
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.h33
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTProtocol.h46
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.c789
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.h59
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.c244
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.h46
-rw-r--r--tts-mqtt-test/src/paho-mqtt/MQTTVersion.c230
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Messages.c104
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Messages.h24
-rw-r--r--tts-mqtt-test/src/paho-mqtt/OsWrapper.c28
-rw-r--r--tts-mqtt-test/src/paho-mqtt/OsWrapper.h42
-rw-r--r--tts-mqtt-test/src/paho-mqtt/SSLSocket.c947
-rw-r--r--tts-mqtt-test/src/paho-mqtt/SSLSocket.h52
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Socket.c1013
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Socket.h145
-rw-r--r--tts-mqtt-test/src/paho-mqtt/SocketBuffer.c415
-rw-r--r--tts-mqtt-test/src/paho-mqtt/SocketBuffer.h84
-rw-r--r--tts-mqtt-test/src/paho-mqtt/StackTrace.c203
-rw-r--r--tts-mqtt-test/src/paho-mqtt/StackTrace.h72
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Thread.c462
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Thread.h73
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Tree.c726
-rw-r--r--tts-mqtt-test/src/paho-mqtt/Tree.h115
-rw-r--r--tts-mqtt-test/src/paho-mqtt/VersionInfo.h7
-rw-r--r--tts-mqtt-test/src/paho-mqtt/mutex_type.h25
-rw-r--r--tts-mqtt-test/src/paho-mqtt/utf-8.c230
-rw-r--r--tts-mqtt-test/src/paho-mqtt/utf-8.h23
-rw-r--r--tts-mqtt-test/src/tts-mqtt-test.c116
-rw-r--r--tts-mqtt-test/tizen-manifest.xml12
60 files changed, 20917 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..00cbbdf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,59 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Typescript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9ccae6e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+# What is MQTT?
+> MQTT (Message Queue Telemetry Transport) is a lightweight publish-subscribe protocol that is used on top of TCP/IP. MQTT uses a message broker that dispatches messages between senders that publish them, and receivers that are interested in these messages. Same client can both publish and subscribe messages.
+
+> Each message is published to a specific topic. The topic is the message routing information, and it is simply a string that can have slash separated hierarchy levels (e.g. “office/floor1/temperature”). Clients subscribe to these topics, and the broker delivers all messages sent with matching topics to them. It is also possible to use wildcards to easily subscribe to multiple topics.
+
+> With this communication scheme, the data producers and consumers do not need to know about each other. They only communicate using a common topic. With cloud systems, the MQTT broker is usually in the cloud, and the ‘things’ connect to it and produce data by publishing messages.
+
+https://pagefault.blog/2017/03/02/using-local-mqtt-broker-for-cloud-and-interprocess-communication/
+
+# Configuration
+- **Broker:** Mosquitto running on AWS Elastic Beanstalk
+- **Tizen Client:** Tizen app based on paho-mqtt (open-source MQTT client library)
+- **Node.js Client:** Node program based on node.js mqtt package
+
+# Prerequisites
+- Install Node.js
+- Install Tizen Studio
+- Install Extension SDK for ARTIK 530s
+- Install corresponding binary on ARTIK 530s device
+- Connect the device to the Internet via WiFi
+
+# Procedure
+
+### 1. Build and run Tizen MQTT client (subscriber role)
+- In Tizen Studio, File > Import > Tizen Project > Next > Browse > tts-mqtt-test > OK > Next > Finish
+- Right click tts-mqtt-test > Build Project
+- Right click tts-mqtt-test > Run As > Tizen Native Application
+```
+sdb dlog MQTT
+```
+
+### 2. Run Node.js MQTT client (publisher role)
+```
+npm install --save mqtt
+node publisher.js
+```
+
+# Deploy My Own MQTT Broker on AWS EB (Advanced)
+
+### 1. Create Elastic Beanstalk Environment with a **Network** Load Balancer
+1. [Install EB CLI (Command-Line Interface)](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-install.html)
+2. [Configure EB CLI](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-configuration.html)
+3. [Create EB Environment and configure a Network Load Balancer using the EB CLI](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-nlb.html)
+
+### 2. Configure the EB Environment for Mosquitto
+1. [Configure Security Group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-network-security.html)
+ * Open port 1883 for all IPs
+2. Configure Load Balancer
+ * AWS > Elastic Beanstalk > Choose the environment > Configuration > Load Balancer > Modify > Add listener > Port: 1883 > Add > Apply
+ -- or --
+ * AWS > EC2 > [Load Balancers](http://console.aws.amazon.com/ec2/v2/home#LoadBalancers) > (Choose the Load Balancer corresponding to the running EC2 instance) > Listeners tab (in the bottom pane) > Add listener > TCP:1883 > Save
+3. Configure Proxy
+### 3. Download [Docker image preconfigured for Mosquitto](https://hub.docker.com/r/ansi/mosquitto/)
+### 4. Upload the Dockerfile to the EB Environment
diff --git a/publisher.js b/publisher.js
new file mode 100644
index 0000000..e2382b5
--- /dev/null
+++ b/publisher.js
@@ -0,0 +1,57 @@
+/* ****************************************************************
+ *
+ * Copyright 2018 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+var mqtt = require('mqtt');
+
+var MQTT_TOPIC = "/seoul-iot/mqtt/test";
+var MQTT_ADDR = "mqtt://MQTTBroker2.us-west-1.elasticbeanstalk.com";
+
+const clientId = 'test-client-' + (Math.random() * 10000).toFixed();
+
+var client = mqtt.connect(MQTT_ADDR, {
+ clientId: clientId,
+ protocolId: 'MQIsdp',
+ protocolVersion: 3,
+ connectTimeout:1000,
+ debug:true
+});
+
+var readline = require('readline');
+
+client.on('connect', function () {
+ console.log(`Connected to ${MQTT_ADDR} ${clientId}!`);
+ console.log(`Enter message to publish for topic "${MQTT_TOPIC}"...`);
+
+ var rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ terminal: false
+ });
+
+ rl.on('line', function(line){
+ client.publish(MQTT_TOPIC, line);
+ });
+});
+
+client.on('message', function (topic, message) {
+ console.log(`'${message.toString()}' received for topic '${topic}'`);
+});
+
+client.on('error', function(){
+ console.log("ERROR")
+})
diff --git a/tts-mqtt-test/.cproject b/tts-mqtt-test/.cproject
new file mode 100644
index 0000000..e70dfc0
--- /dev/null
+++ b/tts-mqtt-test/.cproject
@@ -0,0 +1,373 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+ <storageModule moduleId="org.eclipse.cdt.core.settings">
+ <cconfiguration id="org.tizen.nativecore.config.sbi.gcc45.app.debug.1759120531">
+ <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.tizen.nativecore.config.sbi.gcc45.app.debug.1759120531" moduleId="org.eclipse.cdt.core.settings" name="Debug">
+ <externalSettings/>
+ <extensions>
+ <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+ <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.tizen.nativecore.NativeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ </extensions>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <configuration artifactName="tts-mqtt-test" buildArtefactType="org.tizen.nativecore.buildArtefactType.app" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.tizen.nativecore.buildArtefactType.app,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;" id="org.tizen.nativecore.config.sbi.gcc45.app.debug.1759120531" name="Debug" parent="org.tizen.nativecore.config.sbi.gcc45.app.debug">
+ <folderInfo id="org.tizen.nativecore.config.sbi.gcc45.app.debug.1759120531." name="/" resourcePath="">
+ <toolChain id="org.tizen.nativecore.toolchain.sbi.gcc45.app.debug.157578259" name="Tizen Native Toolchain" superClass="org.tizen.nativecore.toolchain.sbi.gcc45.app.debug">
+ <targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="org.tizen.nativeide.target.sbi.gnu.platform.base.64387210" osList="linux,win32" superClass="org.tizen.nativeide.target.sbi.gnu.platform.base"/>
+ <builder autoBuildTarget="all" buildPath="${workspace_loc:/tts-mqtt-test}/Debug" enableAutoBuild="true" id="org.tizen.nativecore.target.sbi.gnu.builder.1884257458" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Tizen Application Builder" superClass="org.tizen.nativecore.target.sbi.gnu.builder"/>
+ <tool command="arm-linux-gnueabi-ar" id="org.tizen.nativecore.tool.sbi.gnu.archiver.1350851337" name="Archiver" superClass="org.tizen.nativecore.tool.sbi.gnu.archiver"/>
+ <tool command="clang++" id="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler.1641088037" name="C++ Compiler" superClass="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler">
+ <option id="gnu.cpp.compiler.option.optimization.level.1949547077" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
+ <option defaultValue="gnu.cpp.compiler.debugging.level.max" id="sbi.gnu.cpp.compiler.option.debugging.level.core.652299823" name="Debug level" superClass="sbi.gnu.cpp.compiler.option.debugging.level.core" valueType="enumerated"/>
+ <option defaultValue="false" id="sbi.gnu.cpp.compiler.option.misc.pic.core.1565167728" name="-fPIC option" superClass="sbi.gnu.cpp.compiler.option.misc.pic.core" valueType="boolean"/>
+ <option id="sbi.gnu.cpp.compiler.option.2016945040" name="Tizen-Target" superClass="sbi.gnu.cpp.compiler.option" valueType="userObjs">
+ <listOptionValue builtIn="false" value="iot-headless-4.0-device.core_llvm40.armel"/>
+ </option>
+ <option id="sbi.gnu.cpp.compiler.option.frameworks_inc.core.1440205480" name="Tizen-Frameworks-Include-Path" superClass="sbi.gnu.cpp.compiler.option.frameworks_inc.core" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/libxml2&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appcore-agent&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appfw&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/base&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ckm&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dbus-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/device&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dlog&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ecore-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/efl-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1/eina&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eo-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/gio-unix-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/glib-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/iotcon&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/json-glib-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/media&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/minizip&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/network&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/storage&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/system&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/dbus-1.0/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/glib-2.0/include&quot;"/>
+ </option>
+ <option id="sbi.gnu.cpp.compiler.option.frameworks_cflags.core.23864796" name="Tizen-Frameworks-Other-Cflags" superClass="sbi.gnu.cpp.compiler.option.frameworks_cflags.core" valueType="stringList">
+ <listOptionValue builtIn="false" value="${TC_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value="${RS_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value=" -fPIE"/>
+ <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
+ <listOptionValue builtIn="false" value="-mthumb"/>
+ </option>
+ <option id="gnu.cpp.compiler.option.include.paths.204118620" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/inc}&quot;"/>
+ </option>
+ <option id="sbi.gnu.cpp.compiler.option.frameworks.core.2053970188" name="Tizen-Frameworks" superClass="sbi.gnu.cpp.compiler.option.frameworks.core" valueType="userObjs">
+ <listOptionValue builtIn="false" value="Native_API"/>
+ <listOptionValue builtIn="false" value="iot-things"/>
+ </option>
+ <option id="sbi.gnu.cpp.compiler.option.preprocessor.def.deprecation.800907823" name="Defined symbols (-D)" superClass="sbi.gnu.cpp.compiler.option.preprocessor.def.deprecation" valueType="definedSymbols">
+ <listOptionValue builtIn="false" value="TIZEN_DEPRECATION"/>
+ <listOptionValue builtIn="false" value="DEPRECATION_WARNING"/>
+ <listOptionValue builtIn="false" value="_DEBUG"/>
+ </option>
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1769204860" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+ </tool>
+ <tool command="clang" id="org.tizen.nativecore.tool.sbi.gnu.c.compiler.30071504" name="C Compiler" superClass="org.tizen.nativecore.tool.sbi.gnu.c.compiler">
+ <option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.option.optimization.level.1265596969" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level" valueType="enumerated"/>
+ <option defaultValue="gnu.c.debugging.level.max" id="sbi.gnu.c.compiler.option.debugging.level.core.684721087" name="Debug level" superClass="sbi.gnu.c.compiler.option.debugging.level.core" valueType="enumerated"/>
+ <option defaultValue="false" id="sbi.gnu.c.compiler.option.misc.pic.core.2054132579" name="-fPIC option" superClass="sbi.gnu.c.compiler.option.misc.pic.core" valueType="boolean"/>
+ <option id="sbi.gnu.c.compiler.option.1416390440" name="Tizen-Target" superClass="sbi.gnu.c.compiler.option" valueType="userObjs">
+ <listOptionValue builtIn="false" value="iot-headless-4.0-device.core_llvm40.armel"/>
+ </option>
+ <option id="sbi.gnu.c.compiler.option.frameworks_inc.core.1205590614" name="Tizen-Frameworks-Include-Path" superClass="sbi.gnu.c.compiler.option.frameworks_inc.core" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/libxml2&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appcore-agent&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appfw&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/base&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ckm&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dbus-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/device&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dlog&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ecore-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/efl-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1/eina&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eo-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/gio-unix-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/glib-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/iotcon&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/json-glib-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/media&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/minizip&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/network&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/storage&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/system&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/dbus-1.0/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/glib-2.0/include&quot;"/>
+ </option>
+ <option id="sbi.gnu.c.compiler.option.frameworks_cflags.core.1773191973" name="Tizen-Frameworks-Other-Cflags" superClass="sbi.gnu.c.compiler.option.frameworks_cflags.core" useByScannerDiscovery="false" valueType="stringList">
+ <listOptionValue builtIn="false" value="${TC_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value="${RS_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value=" -fPIE"/>
+ <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
+ <listOptionValue builtIn="false" value="-mthumb"/>
+ </option>
+ <option id="gnu.c.compiler.option.include.paths.2147205050" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/inc}&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/paho-mqtt}&quot;"/>
+ </option>
+ <option id="sbi.gnu.c.compiler.option.frameworks.core.868736646" name="Tizen-Frameworks" superClass="sbi.gnu.c.compiler.option.frameworks.core" valueType="userObjs">
+ <listOptionValue builtIn="false" value="Native_API"/>
+ <listOptionValue builtIn="false" value="iot-things"/>
+ </option>
+ <option id="sbi.gnu.c.compiler.option.preprocessor.def.symbols.deprecation.1971034475" name="Defined symbols (-D)" superClass="sbi.gnu.c.compiler.option.preprocessor.def.symbols.deprecation" valueType="definedSymbols">
+ <listOptionValue builtIn="false" value="TIZEN_DEPRECATION"/>
+ <listOptionValue builtIn="false" value="DEPRECATION_WARNING"/>
+ <listOptionValue builtIn="false" value="_DEBUG"/>
+ </option>
+ <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1072767784" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+ </tool>
+ <tool id="org.tizen.nativeide.tool.sbi.gnu.c.linker.base.1881714427" name="C Linker" superClass="org.tizen.nativeide.tool.sbi.gnu.c.linker.base"/>
+ <tool command="clang++" id="org.tizen.nativecore.tool.sbi.gnu.cpp.linker.740503319" name="C++ Linker" superClass="org.tizen.nativecore.tool.sbi.gnu.cpp.linker">
+ <option defaultValue="false" id="sbi.gnu.cpp.link.option.strip.1415658902" name="Omit all symbol information (-s)" superClass="sbi.gnu.cpp.link.option.strip" valueType="boolean"/>
+ <option defaultValue="false" id="sbi.gnu.cpp.linker.option.shared_flag.core.362836497" name="Linker.Shared" superClass="sbi.gnu.cpp.linker.option.shared_flag.core" valueType="boolean"/>
+ <option defaultValue="false" id="sbi.gnu.cpp.linker.option.noundefined.core.1227571934" name="Report unresolved symbol references (-Wl,--no-undefined)" superClass="sbi.gnu.cpp.linker.option.noundefined.core" valueType="boolean"/>
+ <option id="sbi.gnu.cpp.linker.option.frameworks_lflags.core.1678057539" name="Tizen-Frameworks-Other-Lflags" superClass="sbi.gnu.cpp.linker.option.frameworks_lflags.core" useByScannerDiscovery="false" valueType="stringList">
+ <listOptionValue builtIn="false" value="${TC_LINKER_MISC}"/>
+ <listOptionValue builtIn="false" value="${RS_LINKER_MISC}"/>
+ <listOptionValue builtIn="false" value="-pie -lpthread "/>
+ <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
+ <listOptionValue builtIn="false" value="-Xlinker --version-script=&quot;${PROJ_PATH}/.exportMap&quot;"/>
+ <listOptionValue builtIn="false" value="-L&quot;${SBI_SYSROOT}/usr/lib&quot;"/>
+ <listOptionValue builtIn="false" value="-L&quot;${SBI_SYSROOT}/../iot-things-4.0-device.core/usr/lib&quot;"/>
+ <listOptionValue builtIn="false" value="$(RS_LIBRARIES)"/>
+ </option>
+ <option id="gnu.cpp.link.option.paths.226869141" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/lib}&quot;"/>
+ </option>
+ <option id="gnu.cpp.link.option.libs.1590006779" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs"/>
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.2052147244" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+ <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+ <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+ </inputType>
+ </tool>
+ <tool command="#{PLATFORM_DEFAULT_GCC_PREFIX}as" id="org.tizen.nativeapp.tool.sbi.gnu.assembler.base.393326720" name="Assembler" superClass="org.tizen.nativeapp.tool.sbi.gnu.assembler.base">
+ <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1606009638" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+ </tool>
+ <tool id="org.tizen.nativecore.tool.fnmapgen.476264200" name="C FN-Map Generator" superClass="org.tizen.nativecore.tool.fnmapgen"/>
+ <tool id="org.tizen.nativecore.tool.fnmapgen.cpp.282271650" name="C++ FN-Map Generator" superClass="org.tizen.nativecore.tool.fnmapgen.cpp"/>
+ <tool id="org.tizen.nativecore.tool.ast.1473556391" name="C Static Analyzer" superClass="org.tizen.nativecore.tool.ast"/>
+ <tool id="org.tizen.nativecore.tool.ast.cpp.422439080" name="C++ Static Analyzer" superClass="org.tizen.nativecore.tool.ast.cpp"/>
+ <tool id="org.tizen.nativecore.tool.sbi.gnu.archiver.mergelib.171236190" name="Archive Generator" superClass="org.tizen.nativecore.tool.sbi.gnu.archiver.mergelib"/>
+ <tool id="org.tizen.nativecore.tool.sbi.po.compiler.252168714" name="PO Resource Compiler" superClass="org.tizen.nativecore.tool.sbi.po.compiler"/>
+ <tool id="org.tizen.nativecore.tool.sbi.edc.compiler.2022460813" name="EDC Resource Compiler" superClass="org.tizen.nativecore.tool.sbi.edc.compiler"/>
+ </toolChain>
+ </folderInfo>
+ <sourceEntries>
+ <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="inc"/>
+ <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="res"/>
+ <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="shared"/>
+ <entry excluding="paho-mqtt/MQTTVersion.c|paho-mqtt/MQTTAsync.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
+ </sourceEntries>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+ </cconfiguration>
+ <cconfiguration id="org.tizen.nativecore.config.sbi.gcc45.app.release.397964099">
+ <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.tizen.nativecore.config.sbi.gcc45.app.release.397964099" moduleId="org.eclipse.cdt.core.settings" name="Release">
+ <externalSettings/>
+ <extensions>
+ <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+ <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.tizen.nativecore.NativeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ </extensions>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <configuration artifactName="tts-mqtt-test" buildArtefactType="org.tizen.nativecore.buildArtefactType.app" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.tizen.nativecore.buildArtefactType.app,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;" id="org.tizen.nativecore.config.sbi.gcc45.app.release.397964099" name="Release" parent="org.tizen.nativecore.config.sbi.gcc45.app.release">
+ <folderInfo id="org.tizen.nativecore.config.sbi.gcc45.app.release.397964099." name="/" resourcePath="">
+ <toolChain id="org.tizen.nativecore.toolchain.sbi.gcc45.app.release.1712900941" name="Tizen Native Toolchain" superClass="org.tizen.nativecore.toolchain.sbi.gcc45.app.release">
+ <targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="org.tizen.nativeide.target.sbi.gnu.platform.base.1422609247" osList="linux,win32" superClass="org.tizen.nativeide.target.sbi.gnu.platform.base"/>
+ <builder buildPath="${workspace_loc:/tts-mqtt-test}/Release" id="org.tizen.nativecore.target.sbi.gnu.builder.638328354" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Tizen Application Builder" superClass="org.tizen.nativecore.target.sbi.gnu.builder"/>
+ <tool command="arm-linux-gnueabi-ar" id="org.tizen.nativecore.tool.sbi.gnu.archiver.1016651506" name="Archiver" superClass="org.tizen.nativecore.tool.sbi.gnu.archiver"/>
+ <tool command="clang++" id="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler.786282581" name="C++ Compiler" superClass="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler">
+ <option id="gnu.cpp.compiler.option.optimization.level.88573722" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>
+ <option defaultValue="gnu.cpp.compiler.debugging.level.none" id="sbi.gnu.cpp.compiler.option.debugging.level.core.344484293" name="Debug level" superClass="sbi.gnu.cpp.compiler.option.debugging.level.core" valueType="enumerated"/>
+ <option defaultValue="false" id="sbi.gnu.cpp.compiler.option.misc.pic.core.2095676340" name="-fPIC option" superClass="sbi.gnu.cpp.compiler.option.misc.pic.core" valueType="boolean"/>
+ <option id="sbi.gnu.cpp.compiler.option.1627242952" name="Tizen-Target" superClass="sbi.gnu.cpp.compiler.option" valueType="userObjs">
+ <listOptionValue builtIn="false" value="iot-headless-4.0-device.core_llvm40.armel"/>
+ </option>
+ <option id="sbi.gnu.cpp.compiler.option.frameworks_inc.core.115745427" name="Tizen-Frameworks-Include-Path" superClass="sbi.gnu.cpp.compiler.option.frameworks_inc.core" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/libxml2&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appcore-agent&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appfw&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/base&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ckm&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dbus-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/device&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dlog&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ecore-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/efl-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1/eina&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eo-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/gio-unix-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/glib-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/iotcon&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/json-glib-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/media&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/minizip&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/network&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/storage&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/system&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/dbus-1.0/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/glib-2.0/include&quot;"/>
+ </option>
+ <option id="sbi.gnu.cpp.compiler.option.frameworks_cflags.core.11569225" name="Tizen-Frameworks-Other-Cflags" superClass="sbi.gnu.cpp.compiler.option.frameworks_cflags.core" valueType="stringList">
+ <listOptionValue builtIn="false" value="${TC_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value="${RS_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value=" -fPIE"/>
+ <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
+ <listOptionValue builtIn="false" value="-mthumb"/>
+ </option>
+ <option id="gnu.cpp.compiler.option.include.paths.298524550" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/inc}&quot;"/>
+ </option>
+ <option id="sbi.gnu.cpp.compiler.option.frameworks.core.1242540061" name="Tizen-Frameworks" superClass="sbi.gnu.cpp.compiler.option.frameworks.core" valueType="userObjs">
+ <listOptionValue builtIn="false" value="Native_API"/>
+ <listOptionValue builtIn="false" value="iot-things"/>
+ </option>
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1031225346" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+ </tool>
+ <tool command="clang" id="org.tizen.nativecore.tool.sbi.gnu.c.compiler.1618220842" name="C Compiler" superClass="org.tizen.nativecore.tool.sbi.gnu.c.compiler">
+ <option defaultValue="gnu.c.optimization.level.most" id="gnu.c.compiler.option.optimization.level.7249115" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level" valueType="enumerated"/>
+ <option defaultValue="gnu.c.debugging.level.none" id="sbi.gnu.c.compiler.option.debugging.level.core.259194840" name="Debug level" superClass="sbi.gnu.c.compiler.option.debugging.level.core" valueType="enumerated"/>
+ <option defaultValue="false" id="sbi.gnu.c.compiler.option.misc.pic.core.1531346241" name="-fPIC option" superClass="sbi.gnu.c.compiler.option.misc.pic.core" valueType="boolean"/>
+ <option id="sbi.gnu.c.compiler.option.395716783" name="Tizen-Target" superClass="sbi.gnu.c.compiler.option" valueType="userObjs">
+ <listOptionValue builtIn="false" value="iot-headless-4.0-device.core_llvm40.armel"/>
+ </option>
+ <option id="sbi.gnu.c.compiler.option.frameworks_inc.core.41594160" name="Tizen-Frameworks-Include-Path" superClass="sbi.gnu.c.compiler.option.frameworks_inc.core" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/libxml2&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appcore-agent&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/appfw&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/base&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ckm&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dbus-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/device&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/dlog&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/ecore-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/efl-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eina-1/eina&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/eo-1&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/gio-unix-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/glib-2.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/iotcon&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/json-glib-1.0&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/media&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/minizip&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/network&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/storage&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/include/system&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/dbus-1.0/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/glib-2.0/include&quot;"/>
+ </option>
+ <option id="sbi.gnu.c.compiler.option.frameworks_cflags.core.668090761" name="Tizen-Frameworks-Other-Cflags" superClass="sbi.gnu.c.compiler.option.frameworks_cflags.core" valueType="stringList">
+ <listOptionValue builtIn="false" value="${TC_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value="${RS_COMPILER_MISC}"/>
+ <listOptionValue builtIn="false" value=" -fPIE"/>
+ <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
+ <listOptionValue builtIn="false" value="-mthumb"/>
+ </option>
+ <option id="gnu.c.compiler.option.include.paths.726586777" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/inc}&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/paho-mqtt}&quot;"/>
+ </option>
+ <option id="sbi.gnu.c.compiler.option.frameworks.core.2037306725" name="Tizen-Frameworks" superClass="sbi.gnu.c.compiler.option.frameworks.core" valueType="userObjs">
+ <listOptionValue builtIn="false" value="Native_API"/>
+ <listOptionValue builtIn="false" value="iot-things"/>
+ </option>
+ <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1943863998" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+ </tool>
+ <tool id="org.tizen.nativeide.tool.sbi.gnu.c.linker.base.192295754" name="C Linker" superClass="org.tizen.nativeide.tool.sbi.gnu.c.linker.base"/>
+ <tool command="clang++" id="org.tizen.nativecore.tool.sbi.gnu.cpp.linker.1301262152" name="C++ Linker" superClass="org.tizen.nativecore.tool.sbi.gnu.cpp.linker">
+ <option defaultValue="true" id="sbi.gnu.cpp.link.option.strip.755741923" name="Omit all symbol information (-s)" superClass="sbi.gnu.cpp.link.option.strip" valueType="boolean"/>
+ <option defaultValue="false" id="sbi.gnu.cpp.linker.option.shared_flag.core.1860973907" name="Linker.Shared" superClass="sbi.gnu.cpp.linker.option.shared_flag.core" valueType="boolean"/>
+ <option defaultValue="false" id="sbi.gnu.cpp.linker.option.noundefined.core.2023827109" name="Report unresolved symbol references (-Wl,--no-undefined)" superClass="sbi.gnu.cpp.linker.option.noundefined.core" valueType="boolean"/>
+ <option id="sbi.gnu.cpp.linker.option.frameworks_lflags.core.1530521786" name="Tizen-Frameworks-Other-Lflags" superClass="sbi.gnu.cpp.linker.option.frameworks_lflags.core" valueType="stringList">
+ <listOptionValue builtIn="false" value="${TC_LINKER_MISC}"/>
+ <listOptionValue builtIn="false" value="${RS_LINKER_MISC}"/>
+ <listOptionValue builtIn="false" value="-pie -lpthread "/>
+ <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
+ <listOptionValue builtIn="false" value="-Xlinker --version-script=&quot;${PROJ_PATH}/.exportMap&quot;"/>
+ <listOptionValue builtIn="false" value="-L&quot;${SBI_SYSROOT}/usr/lib&quot;"/>
+ <listOptionValue builtIn="false" value="-L&quot;${SBI_SYSROOT}/../iot-things-4.0-device.core/usr/lib&quot;"/>
+ <listOptionValue builtIn="false" value="$(RS_LIBRARIES)"/>
+ </option>
+ <option id="gnu.cpp.link.option.paths.2073716909" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/lib}&quot;"/>
+ </option>
+ <option id="gnu.cpp.link.option.libs.660583870" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" useByScannerDiscovery="false" valueType="libs"/>
+ <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.824716900" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+ <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+ <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+ </inputType>
+ </tool>
+ <tool command="#{PLATFORM_DEFAULT_GCC_PREFIX}as" id="org.tizen.nativeapp.tool.sbi.gnu.assembler.base.93717664" name="Assembler" superClass="org.tizen.nativeapp.tool.sbi.gnu.assembler.base">
+ <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1741316422" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+ </tool>
+ <tool id="org.tizen.nativecore.tool.fnmapgen.455709683" name="C FN-Map Generator" superClass="org.tizen.nativecore.tool.fnmapgen"/>
+ <tool id="org.tizen.nativecore.tool.fnmapgen.cpp.1494934668" name="C++ FN-Map Generator" superClass="org.tizen.nativecore.tool.fnmapgen.cpp"/>
+ <tool id="org.tizen.nativecore.tool.ast.127405757" name="C Static Analyzer" superClass="org.tizen.nativecore.tool.ast"/>
+ <tool id="org.tizen.nativecore.tool.ast.cpp.2079263297" name="C++ Static Analyzer" superClass="org.tizen.nativecore.tool.ast.cpp"/>
+ <tool id="org.tizen.nativecore.tool.sbi.gnu.archiver.mergelib.829410397" name="Archive Generator" superClass="org.tizen.nativecore.tool.sbi.gnu.archiver.mergelib"/>
+ <tool id="org.tizen.nativecore.tool.sbi.po.compiler.1894412204" name="PO Resource Compiler" superClass="org.tizen.nativecore.tool.sbi.po.compiler"/>
+ <tool id="org.tizen.nativecore.tool.sbi.edc.compiler.1001280617" name="EDC Resource Compiler" superClass="org.tizen.nativecore.tool.sbi.edc.compiler"/>
+ </toolChain>
+ </folderInfo>
+ <sourceEntries>
+ <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="inc"/>
+ <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="res"/>
+ <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="shared"/>
+ <entry excluding="paho-mqtt/MQTTVersion.c|paho-mqtt/MQTTAsync.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
+ </sourceEntries>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+ </cconfiguration>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <project id="tts-mqtt-test.org.tizen.nativecore.target.sbi.gcc45.app.1556645369" name="Tizen Native Application" projectType="org.tizen.nativecore.target.sbi.gcc45.app"/>
+ </storageModule>
+ <storageModule moduleId="scannerConfiguration">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ <scannerConfigBuildInfo instanceId="org.tizen.nativecore.config.sbi.gcc45.app.release.397964099">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ </scannerConfigBuildInfo>
+ <scannerConfigBuildInfo instanceId="org.tizen.nativecore.config.sbi.gcc45.app.debug.1759120531">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ </scannerConfigBuildInfo>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+ <storageModule moduleId="refreshScope" versionNumber="2">
+ <configuration configurationName="Debug">
+ <resource resourceType="PROJECT" workspacePath="/tts-mqtt-test"/>
+ </configuration>
+ <configuration configurationName="Release">
+ <resource resourceType="PROJECT" workspacePath="/tts-mqtt-test"/>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
+</cproject>
diff --git a/tts-mqtt-test/.exportMap b/tts-mqtt-test/.exportMap
new file mode 100644
index 0000000..de30516
--- /dev/null
+++ b/tts-mqtt-test/.exportMap
@@ -0,0 +1,5 @@
+{
+ global: main;
+ _IO_*;
+ local: *;
+};
diff --git a/tts-mqtt-test/.gitignore b/tts-mqtt-test/.gitignore
new file mode 100644
index 0000000..ec5a4db
--- /dev/null
+++ b/tts-mqtt-test/.gitignore
@@ -0,0 +1,6 @@
+.sign/
+res/certificate.pem
+res/privatekey.der
+
+/Debug/
+/SA_Report/
diff --git a/tts-mqtt-test/.project b/tts-mqtt-test/.project
new file mode 100644
index 0000000..387ddad
--- /dev/null
+++ b/tts-mqtt-test/.project
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>tts-mqtt-test</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+ <triggers>full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ <nature>org.eclipse.cdt.core.ccnature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+ </natures>
+ <filteredResources>
+ <filter>
+ <id>1533531563605</id>
+ <name></name>
+ <type>26</type>
+ <matcher>
+ <id>org.eclipse.ui.ide.multiFilter</id>
+ <arguments>1.0-projectRelativePath-matches-false-false-*/.tpk</arguments>
+ </matcher>
+ </filter>
+ <filter>
+ <id>1533531563608</id>
+ <name></name>
+ <type>6</type>
+ <matcher>
+ <id>org.eclipse.ui.ide.multiFilter</id>
+ <arguments>1.0-name-matches-false-false-project_def.prop</arguments>
+ </matcher>
+ </filter>
+ </filteredResources>
+</projectDescription>
diff --git a/tts-mqtt-test/.tproject b/tts-mqtt-test/.tproject
new file mode 100644
index 0000000..f857534
--- /dev/null
+++ b/tts-mqtt-test/.tproject
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<tproject xmlns="http://www.tizen.org/tproject">
+ <platforms>
+ <platform>
+ <name>iot-headless-4.0</name>
+ </platform>
+ </platforms>
+ <package>
+ <blacklist/>
+ <resFallback autoGen="true"/>
+ </package>
+</tproject>
diff --git a/tts-mqtt-test/inc/log.h b/tts-mqtt-test/inc/log.h
new file mode 100644
index 0000000..ecbbc59
--- /dev/null
+++ b/tts-mqtt-test/inc/log.h
@@ -0,0 +1,45 @@
+/* ****************************************************************
+ *
+ * Copyright 2018 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include <dlog.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "MQTT"
+
+#define ERR(fmt, args...) dlog_print(DLOG_ERROR, LOG_TAG, "%s : %s(%d) > "fmt"\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##args)
+#define WARN(fmt, args...) dlog_print(DLOG_WARN, LOG_TAG, "%s : %s(%d) > "fmt"\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##args)
+#define INFO(fmt, args...) dlog_print(DLOG_INFO, LOG_TAG, "%s : %s(%d) > "fmt"\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##args)
+#define DBG(fmt, args...) dlog_print(DLOG_DEBUG, LOG_TAG, "%s : %s(%d) > "fmt"\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##args)
+
+#define FN_CALL dlog_print(DLOG_DEBUG, LOG_TAG, ">>>>>>>> called")
+#define FN_END dlog_print(DLOG_DEBUG, LOG_TAG, "<<<<<<<< ended")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LOG_H__ */
diff --git a/tts-mqtt-test/project_def.prop b/tts-mqtt-test/project_def.prop
new file mode 100644
index 0000000..6a51335
--- /dev/null
+++ b/tts-mqtt-test/project_def.prop
@@ -0,0 +1,11 @@
+APPNAME = tts-mqtt-test
+
+type = app
+profile = iot-headless-4.0
+
+USER_SRCS = src/tts-mqtt-test.c
+USER_DEFS =
+USER_INC_DIRS = inc, inc/sdk
+USER_OBJS =
+USER_LIBS =
+USER_EDCS =
diff --git a/tts-mqtt-test/shared/res/tts-mqtt-test.png b/tts-mqtt-test/shared/res/tts-mqtt-test.png
new file mode 100644
index 0000000..9765b1b
--- /dev/null
+++ b/tts-mqtt-test/shared/res/tts-mqtt-test.png
Binary files differ
diff --git a/tts-mqtt-test/src/paho-mqtt/Clients.c b/tts-mqtt-test/src/paho-mqtt/Clients.c
new file mode 100644
index 0000000..b5e00ed
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Clients.c
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - add SSL support
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief functions which apply to client structures
+ * */
+
+
+#include "../paho-mqtt/Clients.h"
+
+#include <string.h>
+#include <stdio.h>
+
+
+/**
+ * List callback function for comparing clients by clientid
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+int clientIDCompare(void* a, void* b)
+{
+ Clients* client = (Clients*)a;
+ /*printf("comparing clientdIDs %s with %s\n", client->clientID, (char*)b);*/
+ return strcmp(client->clientID, (char*)b) == 0;
+}
+
+
+/**
+ * List callback function for comparing clients by socket
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+int clientSocketCompare(void* a, void* b)
+{
+ Clients* client = (Clients*)a;
+ /*printf("comparing %d with %d\n", (char*)a, (char*)b); */
+ return client->net.socket == *(int*)b;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/Clients.h b/tts-mqtt-test/src/paho-mqtt/Clients.h
new file mode 100644
index 0000000..47189d9
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Clients.h
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - add SSL support
+ * Ian Craggs - fix for bug 413429 - connectionLost not called
+ * Ian Craggs - change will payload to binary
+ * Ian Craggs - password to binary
+ *******************************************************************************/
+
+#if !defined(CLIENTS_H)
+#define CLIENTS_H
+
+#include <time.h>
+#if defined(OPENSSL)
+#if defined(WIN32) || defined(WIN64)
+#include <winsock2.h>
+#endif
+#include <openssl/ssl.h>
+#endif
+#include "../paho-mqtt/MQTTClient.h"
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/MQTTClientPersistence.h"
+/*BE
+include "LinkedList"
+BE*/
+
+/*BE
+def PUBLICATIONS
+{
+ n32 ptr STRING open "topic"
+ n32 ptr DATA "payload"
+ n32 dec "payloadlen"
+ n32 dec "refcount"
+}
+BE*/
+/**
+ * Stored publication data to minimize copying
+ */
+typedef struct
+{
+ char *topic;
+ int topiclen;
+ char* payload;
+ int payloadlen;
+ int refcount;
+} Publications;
+
+/*BE
+// This should get moved to MQTTProtocol, but the includes don't quite work yet
+map MESSAGE_TYPES
+{
+ "PUBREC" 5
+ "PUBREL" .
+ "PUBCOMP" .
+}
+
+
+def MESSAGES
+{
+ n32 dec "qos"
+ n32 map bool "retain"
+ n32 dec "msgid"
+ n32 ptr PUBLICATIONS "publish"
+ n32 time "lastTouch"
+ n8 map MESSAGE_TYPES "nextMessageType"
+ n32 dec "len"
+}
+defList(MESSAGES)
+BE*/
+/**
+ * Client publication message data
+ */
+typedef struct
+{
+ int qos;
+ int retain;
+ int msgid;
+ Publications *publish;
+ time_t lastTouch; /**> used for retry and expiry */
+ char nextMessageType; /**> PUBREC, PUBREL, PUBCOMP */
+ int len; /**> length of the whole structure+data */
+} Messages;
+
+
+/*BE
+def WILLMESSAGES
+{
+ n32 ptr STRING open "topic"
+ n32 ptr DATA open "msg"
+ n32 dec "retained"
+ n32 dec "qos"
+}
+BE*/
+
+/**
+ * Client will message data
+ */
+typedef struct
+{
+ char *topic;
+ int payloadlen;
+ void *payload;
+ int retained;
+ int qos;
+} willMessages;
+
+/*BE
+map CLIENT_BITS
+{
+ "cleansession" 1 : .
+ "connected" 2 : .
+ "good" 4 : .
+ "ping_outstanding" 8 : .
+}
+def CLIENTS
+{
+ n32 ptr STRING open "clientID"
+ n32 ptr STRING open "username"
+ n32 ptr STRING open "password"
+ n32 map CLIENT_BITS "bits"
+ at 4 n8 bits 7:6 dec "connect_state"
+ at 8
+ n32 dec "socket"
+ n32 ptr "SSL"
+ n32 dec "msgID"
+ n32 dec "keepAliveInterval"
+ n32 dec "maxInflightMessages"
+ n32 ptr BRIDGECONNECTIONS "bridge_context"
+ n32 time "lastContact"
+ n32 ptr WILLMESSAGES "will"
+ n32 ptr MESSAGESList open "inboundMsgs"
+ n32 ptr MESSAGESList open "outboundMsgs"
+ n32 ptr MESSAGESList open "messageQueue"
+ n32 dec "discardedMsgs"
+}
+
+defList(CLIENTS)
+
+BE*/
+
+typedef struct
+{
+ int socket;
+ time_t lastSent;
+ time_t lastReceived;
+#if defined(OPENSSL)
+ SSL* ssl;
+ SSL_CTX* ctx;
+#endif
+} networkHandles;
+
+/**
+ * Data related to one client
+ */
+typedef struct
+{
+ char* clientID; /**< the string id of the client */
+ const char* username; /**< MQTT v3.1 user name */
+ int passwordlen; /**< MQTT password length */
+ const void* password; /**< MQTT v3.1 binary password */
+ unsigned int cleansession : 1; /**< MQTT clean session flag */
+ unsigned int connected : 1; /**< whether it is currently connected */
+ unsigned int good : 1; /**< if we have an error on the socket we turn this off */
+ unsigned int ping_outstanding : 1;
+ int connect_state : 4;
+ networkHandles net;
+ int msgID;
+ int keepAliveInterval;
+ int retryInterval;
+ int maxInflightMessages;
+ willMessages* will;
+ List* inboundMsgs;
+ List* outboundMsgs; /**< in flight */
+ List* messageQueue;
+ unsigned int qentry_seqno;
+ void* phandle; /* the persistence handle */
+ MQTTClient_persistence* persistence; /* a persistence implementation */
+ void* context; /* calling context - used when calling disconnect_internal */
+ int MQTTVersion;
+#if defined(OPENSSL)
+ MQTTClient_SSLOptions *sslopts;
+ SSL_SESSION* session; /***< SSL session pointer for fast handhake */
+#endif
+} Clients;
+
+int clientIDCompare(void* a, void* b);
+int clientSocketCompare(void* a, void* b);
+
+/**
+ * Configuration data related to all clients
+ */
+typedef struct
+{
+ const char* version;
+ List* clients;
+} ClientStates;
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/Heap.c b/tts-mqtt-test/src/paho-mqtt/Heap.c
new file mode 100644
index 0000000..4094c5f
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Heap.c
@@ -0,0 +1,493 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - use tree data structure instead of list
+ * Ian Craggs - change roundup to Heap_roundup to avoid macro name clash on MacOSX
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief functions to manage the heap with the goal of eliminating memory leaks
+ *
+ * For any module to use these functions transparently, simply include the Heap.h
+ * header file. Malloc and free will be redefined, but will behave in exactly the same
+ * way as normal, so no recoding is necessary.
+ *
+ * */
+
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/StackTrace.h"
+#include "../paho-mqtt/Thread.h"
+#include "../paho-mqtt/Tree.h"
+
+#if defined(HEAP_UNIT_TESTS)
+char* Broker_recordFFDC(char* symptoms);
+#endif /* HEAP_UNIT_TESTS */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "../paho-mqtt/Heap.h"
+
+#undef malloc
+#undef realloc
+#undef free
+
+#if defined(WIN32) || defined(WIN64)
+mutex_type heap_mutex;
+#else
+static pthread_mutex_t heap_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type heap_mutex = &heap_mutex_store;
+#endif
+
+static heap_info state = {0, 0}; /**< global heap state information */
+static int eyecatcher = 0x88888888;
+
+/**
+ * Each item on the heap is recorded with this structure.
+ */
+typedef struct
+{
+ char* file; /**< the name of the source file where the storage was allocated */
+ int line; /**< the line no in the source file where it was allocated */
+ void* ptr; /**< pointer to the allocated storage */
+ size_t size; /**< size of the allocated storage */
+} storageElement;
+
+static Tree heap; /**< Tree that holds the allocation records */
+static const char *errmsg = "Memory allocation error";
+
+
+static size_t Heap_roundup(size_t size);
+static int ptrCompare(void* a, void* b, int value);
+/*static void Heap_check(char* string, void* ptr);*/
+static void checkEyecatchers(char* file, int line, void* p, size_t size);
+static int Internal_heap_unlink(char* file, int line, void* p);
+static void HeapScan(enum LOG_LEVELS log_level);
+
+
+/**
+ * Round allocation size up to a multiple of the size of an int. Apart from possibly reducing fragmentation,
+ * on the old v3 gcc compilers I was hitting some weird behaviour, which might have been errors in
+ * sizeof() used on structures and related to packing. In any case, this fixes that too.
+ * @param size the size actually needed
+ * @return the rounded up size
+ */
+static size_t Heap_roundup(size_t size)
+{
+ static int multsize = 4*sizeof(int);
+
+ if (size % multsize != 0)
+ size += multsize - (size % multsize);
+ return size;
+}
+
+
+/**
+ * List callback function for comparing storage elements
+ * @param a pointer to the current content in the tree (storageElement*)
+ * @param b pointer to the memory to free
+ * @return boolean indicating whether a and b are equal
+ */
+static int ptrCompare(void* a, void* b, int value)
+{
+ a = ((storageElement*)a)->ptr;
+ if (value)
+ b = ((storageElement*)b)->ptr;
+
+ return (a > b) ? -1 : (a == b) ? 0 : 1;
+}
+
+/*
+static void Heap_check(char* string, void* ptr)
+{
+ Node* curnode = NULL;
+ storageElement* prev, *s = NULL;
+
+ printf("Heap_check start %p\n", ptr);
+ while ((curnode = TreeNextElement(&heap, curnode)) != NULL)
+ {
+ prev = s;
+ s = (storageElement*)(curnode->content);
+
+ if (prev)
+ {
+ if (ptrCompare(s, prev, 1) != -1)
+ {
+ printf("%s: heap order error %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
+ exit(99);
+ }
+ else
+ printf("%s: heap order good %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
+ }
+ }
+}*/
+
+
+/**
+ * Allocates a block of memory. A direct replacement for malloc, but keeps track of items
+ * allocated in a list, so that free can check that a item is being freed correctly and that
+ * we can check that all memory is freed at shutdown.
+ * @param file use the __FILE__ macro to indicate which file this item was allocated in
+ * @param line use the __LINE__ macro to indicate which line this item was allocated at
+ * @param size the size of the item to be allocated
+ * @return pointer to the allocated item, or NULL if there was an error
+ */
+void* mymalloc(char* file, int line, size_t size)
+{
+ storageElement* s = NULL;
+ size_t space = sizeof(storageElement);
+ size_t filenamelen = strlen(file)+1;
+
+ Thread_lock_mutex(heap_mutex);
+ size = Heap_roundup(size);
+ if ((s = malloc(sizeof(storageElement))) == NULL)
+ {
+ Log(LOG_ERROR, 13, errmsg);
+ return NULL;
+ }
+ s->size = size; /* size without eyecatchers */
+ if ((s->file = malloc(filenamelen)) == NULL)
+ {
+ Log(LOG_ERROR, 13, errmsg);
+ free(s);
+ return NULL;
+ }
+ space += filenamelen;
+ strcpy(s->file, file);
+ s->line = line;
+ /* Add space for eyecatcher at each end */
+ if ((s->ptr = malloc(size + 2*sizeof(int))) == NULL)
+ {
+ Log(LOG_ERROR, 13, errmsg);
+ free(s->file);
+ free(s);
+ return NULL;
+ }
+ space += size + 2*sizeof(int);
+ *(int*)(s->ptr) = eyecatcher; /* start eyecatcher */
+ *(int*)(((char*)(s->ptr)) + (sizeof(int) + size)) = eyecatcher; /* end eyecatcher */
+ Log(TRACE_MAX, -1, "Allocating %d bytes in heap at file %s line %d ptr %p\n", size, file, line, s->ptr);
+ TreeAdd(&heap, s, space);
+ state.current_size += size;
+ if (state.current_size > state.max_size)
+ state.max_size = state.current_size;
+ Thread_unlock_mutex(heap_mutex);
+ return ((int*)(s->ptr)) + 1; /* skip start eyecatcher */
+}
+
+
+static void checkEyecatchers(char* file, int line, void* p, size_t size)
+{
+ int *sp = (int*)p;
+ char *cp = (char*)p;
+ int us;
+ static const char *msg = "Invalid %s eyecatcher %d in heap item at file %s line %d";
+
+ if ((us = *--sp) != eyecatcher)
+ Log(LOG_ERROR, 13, msg, "start", us, file, line);
+
+ cp += size;
+ if ((us = *(int*)cp) != eyecatcher)
+ Log(LOG_ERROR, 13, msg, "end", us, file, line);
+}
+
+
+/**
+ * Remove an item from the recorded heap without actually freeing it.
+ * Use sparingly!
+ * @param file use the __FILE__ macro to indicate which file this item was allocated in
+ * @param line use the __LINE__ macro to indicate which line this item was allocated at
+ * @param p pointer to the item to be removed
+ */
+static int Internal_heap_unlink(char* file, int line, void* p)
+{
+ Node* e = NULL;
+ int rc = 0;
+
+ e = TreeFind(&heap, ((int*)p)-1);
+ if (e == NULL)
+ Log(LOG_ERROR, 13, "Failed to remove heap item at file %s line %d", file, line);
+ else
+ {
+ storageElement* s = (storageElement*)(e->content);
+ Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes\n",
+ s->size, file, line, state.current_size);
+ checkEyecatchers(file, line, p, s->size);
+ /* free(s->ptr); */
+ free(s->file);
+ state.current_size -= s->size;
+ TreeRemoveNodeIndex(&heap, e, 0);
+ free(s);
+ rc = 1;
+ }
+ return rc;
+}
+
+
+/**
+ * Frees a block of memory. A direct replacement for free, but checks that a item is in
+ * the allocates list first.
+ * @param file use the __FILE__ macro to indicate which file this item was allocated in
+ * @param line use the __LINE__ macro to indicate which line this item was allocated at
+ * @param p pointer to the item to be freed
+ */
+void myfree(char* file, int line, void* p)
+{
+ if (p) /* it is legal und usual to call free(NULL) */
+ {
+ Thread_lock_mutex(heap_mutex);
+ if (Internal_heap_unlink(file, line, p))
+ free(((int*)p)-1);
+ Thread_unlock_mutex(heap_mutex);
+ }
+ else
+ {
+ Log(LOG_ERROR, -1, "Call of free(NULL) in %s,%d",file,line);
+ }
+}
+
+
+/**
+ * Remove an item from the recorded heap without actually freeing it.
+ * Use sparingly!
+ * @param file use the __FILE__ macro to indicate which file this item was allocated in
+ * @param line use the __LINE__ macro to indicate which line this item was allocated at
+ * @param p pointer to the item to be removed
+ */
+void Heap_unlink(char* file, int line, void* p)
+{
+ Thread_lock_mutex(heap_mutex);
+ Internal_heap_unlink(file, line, p);
+ Thread_unlock_mutex(heap_mutex);
+}
+
+
+/**
+ * Reallocates a block of memory. A direct replacement for realloc, but keeps track of items
+ * allocated in a list, so that free can check that a item is being freed correctly and that
+ * we can check that all memory is freed at shutdown.
+ * We have to remove the item from the tree, as the memory is in order and so it needs to
+ * be reinserted in the correct place.
+ * @param file use the __FILE__ macro to indicate which file this item was reallocated in
+ * @param line use the __LINE__ macro to indicate which line this item was reallocated at
+ * @param p pointer to the item to be reallocated
+ * @param size the new size of the item
+ * @return pointer to the allocated item, or NULL if there was an error
+ */
+void *myrealloc(char* file, int line, void* p, size_t size)
+{
+ void* rc = NULL;
+ storageElement* s = NULL;
+
+ Thread_lock_mutex(heap_mutex);
+ s = TreeRemoveKey(&heap, ((int*)p)-1);
+ if (s == NULL)
+ Log(LOG_ERROR, 13, "Failed to reallocate heap item at file %s line %d", file, line);
+ else
+ {
+ size_t space = sizeof(storageElement);
+ size_t filenamelen = strlen(file)+1;
+
+ checkEyecatchers(file, line, p, s->size);
+ size = Heap_roundup(size);
+ state.current_size += size - s->size;
+ if (state.current_size > state.max_size)
+ state.max_size = state.current_size;
+ if ((s->ptr = realloc(s->ptr, size + 2*sizeof(int))) == NULL)
+ {
+ Log(LOG_ERROR, 13, errmsg);
+ return NULL;
+ }
+ space += size + 2*sizeof(int) - s->size;
+ *(int*)(s->ptr) = eyecatcher; /* start eyecatcher */
+ *(int*)(((char*)(s->ptr)) + (sizeof(int) + size)) = eyecatcher; /* end eyecatcher */
+ s->size = size;
+ space -= strlen(s->file);
+ s->file = realloc(s->file, filenamelen);
+ space += filenamelen;
+ strcpy(s->file, file);
+ s->line = line;
+ rc = s->ptr;
+ TreeAdd(&heap, s, space);
+ }
+ Thread_unlock_mutex(heap_mutex);
+ return (rc == NULL) ? NULL : ((int*)(rc)) + 1; /* skip start eyecatcher */
+}
+
+
+/**
+ * Utility to find an item in the heap. Lets you know if the heap already contains
+ * the memory location in question.
+ * @param p pointer to a memory location
+ * @return pointer to the storage element if found, or NULL
+ */
+void* Heap_findItem(void* p)
+{
+ Node* e = NULL;
+
+ Thread_lock_mutex(heap_mutex);
+ e = TreeFind(&heap, ((int*)p)-1);
+ Thread_unlock_mutex(heap_mutex);
+ return (e == NULL) ? NULL : e->content;
+}
+
+
+/**
+ * Scans the heap and reports any items currently allocated.
+ * To be used at shutdown if any heap items have not been freed.
+ */
+static void HeapScan(enum LOG_LEVELS log_level)
+{
+ Node* current = NULL;
+
+ Thread_lock_mutex(heap_mutex);
+ Log(log_level, -1, "Heap scan start, total %d bytes", state.current_size);
+ while ((current = TreeNextElement(&heap, current)) != NULL)
+ {
+ storageElement* s = (storageElement*)(current->content);
+ Log(log_level, -1, "Heap element size %d, line %d, file %s, ptr %p", s->size, s->line, s->file, s->ptr);
+ Log(log_level, -1, " Content %*.s", (10 > current->size) ? s->size : 10, (char*)(((int*)s->ptr) + 1));
+ }
+ Log(log_level, -1, "Heap scan end");
+ Thread_unlock_mutex(heap_mutex);
+}
+
+
+/**
+ * Heap initialization.
+ */
+int Heap_initialize(void)
+{
+ TreeInitializeNoMalloc(&heap, ptrCompare);
+ heap.heap_tracking = 0; /* no recursive heap tracking! */
+ return 0;
+}
+
+
+/**
+ * Heap termination.
+ */
+void Heap_terminate(void)
+{
+ Log(TRACE_MIN, -1, "Maximum heap use was %d bytes", state.max_size);
+ if (state.current_size > 20) /* One log list is freed after this function is called */
+ {
+ Log(LOG_ERROR, -1, "Some memory not freed at shutdown, possible memory leak");
+ HeapScan(LOG_ERROR);
+ }
+}
+
+
+/**
+ * Access to heap state
+ * @return pointer to the heap state structure
+ */
+heap_info* Heap_get_info(void)
+{
+ return &state;
+}
+
+
+/**
+ * Dump a string from the heap so that it can be displayed conveniently
+ * @param file file handle to dump the heap contents to
+ * @param str the string to dump, could be NULL
+ */
+int HeapDumpString(FILE* file, char* str)
+{
+ int rc = 0;
+ size_t len = str ? strlen(str) + 1 : 0; /* include the trailing null */
+
+ if (fwrite(&(str), sizeof(char*), 1, file) != 1)
+ rc = -1;
+ else if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
+ rc = -1;
+ else if (len > 0 && fwrite(str, len, 1, file) != 1)
+ rc = -1;
+ return rc;
+}
+
+
+/**
+ * Dump the state of the heap
+ * @param file file handle to dump the heap contents to
+ */
+int HeapDump(FILE* file)
+{
+ int rc = 0;
+ Node* current = NULL;
+
+ while (rc == 0 && (current = TreeNextElement(&heap, current)))
+ {
+ storageElement* s = (storageElement*)(current->content);
+
+ if (fwrite(&(s->ptr), sizeof(s->ptr), 1, file) != 1)
+ rc = -1;
+ else if (fwrite(&(current->size), sizeof(current->size), 1, file) != 1)
+ rc = -1;
+ else if (fwrite(s->ptr, current->size, 1, file) != 1)
+ rc = -1;
+ }
+ return rc;
+}
+
+
+#if defined(HEAP_UNIT_TESTS)
+
+void Log(enum LOG_LEVELS log_level, int msgno, char* format, ...)
+{
+ printf("Log %s", format);
+}
+
+char* Broker_recordFFDC(char* symptoms)
+{
+ printf("recordFFDC");
+ return "";
+}
+
+#define malloc(x) mymalloc(__FILE__, __LINE__, x)
+#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
+#define free(x) myfree(__FILE__, __LINE__, x)
+
+int main(int argc, char *argv[])
+{
+ char* h = NULL;
+ Heap_initialize();
+
+ h = malloc(12);
+ free(h);
+ printf("freed h\n");
+
+ h = malloc(12);
+ h = realloc(h, 14);
+ h = realloc(h, 25);
+ h = realloc(h, 255);
+ h = realloc(h, 2225);
+ h = realloc(h, 22225);
+ printf("freeing h\n");
+ free(h);
+ Heap_terminate();
+ printf("Finishing\n");
+ return 0;
+}
+
+#endif /* HEAP_UNIT_TESTS */
+
+/* Local Variables: */
+/* indent-tabs-mode: t */
+/* c-basic-offset: 8 */
+/* End: */
diff --git a/tts-mqtt-test/src/paho-mqtt/Heap.h b/tts-mqtt-test/src/paho-mqtt/Heap.h
new file mode 100644
index 0000000..6d24c04
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Heap.h
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - use tree data structure instead of list
+ *******************************************************************************/
+
+
+#if !defined(HEAP_H)
+#define HEAP_H
+
+#if defined(HIGH_PERFORMANCE)
+#define NO_HEAP_TRACKING 1
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if !defined(NO_HEAP_TRACKING)
+/**
+ * redefines malloc to use "mymalloc" so that heap allocation can be tracked
+ * @param x the size of the item to be allocated
+ * @return the pointer to the item allocated, or NULL
+ */
+#define malloc(x) mymalloc(__FILE__, __LINE__, x)
+
+/**
+ * redefines realloc to use "myrealloc" so that heap allocation can be tracked
+ * @param a the heap item to be reallocated
+ * @param b the new size of the item
+ * @return the new pointer to the heap item
+ */
+#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
+
+/**
+ * redefines free to use "myfree" so that heap allocation can be tracked
+ * @param x the size of the item to be freed
+ */
+#define free(x) myfree(__FILE__, __LINE__, x)
+
+#endif
+
+/**
+ * Information about the state of the heap.
+ */
+typedef struct
+{
+ size_t current_size; /**< current size of the heap in bytes */
+ size_t max_size; /**< max size the heap has reached in bytes */
+} heap_info;
+
+#if defined(__cplusplus)
+ extern "C" {
+#endif
+
+void* mymalloc(char*, int, size_t size);
+void* myrealloc(char*, int, void* p, size_t size);
+void myfree(char*, int, void* p);
+
+void Heap_scan(FILE* file);
+int Heap_initialize(void);
+void Heap_terminate(void);
+heap_info* Heap_get_info(void);
+int HeapDump(FILE* file);
+int HeapDumpString(FILE* file, char* str);
+void* Heap_findItem(void* p);
+void Heap_unlink(char* file, int line, void* p);
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/LinkedList.c b/tts-mqtt-test/src/paho-mqtt/LinkedList.c
new file mode 100644
index 0000000..1228c7d
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/LinkedList.c
@@ -0,0 +1,506 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - updates for the async client
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief functions which apply to linked list structures.
+ *
+ * These linked lists can hold data of any sort, pointed to by the content pointer of the
+ * ListElement structure. ListElements hold the points to the next and previous items in the
+ * list.
+ * */
+
+#include "../paho-mqtt/LinkedList.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../paho-mqtt/Heap.h"
+
+
+static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent);
+
+
+/**
+ * Sets a list structure to empty - all null values. Does not remove any items from the list.
+ * @param newl a pointer to the list structure to be initialized
+ */
+void ListZero(List* newl)
+{
+ memset(newl, '\0', sizeof(List));
+ /*newl->first = NULL;
+ newl->last = NULL;
+ newl->current = NULL;
+ newl->count = newl->size = 0;*/
+}
+
+
+/**
+ * Allocates and initializes a new list structure.
+ * @return a pointer to the new list structure
+ */
+List* ListInitialize(void)
+{
+ List* newl = malloc(sizeof(List));
+ ListZero(newl);
+ return newl;
+}
+
+
+/**
+ * Append an already allocated ListElement and content to a list. Can be used to move
+ * an item from one list to another.
+ * @param aList the list to which the item is to be added
+ * @param content the list item content itself
+ * @param newel the ListElement to be used in adding the new item
+ * @param size the size of the element
+ */
+void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size)
+{ /* for heap use */
+ newel->content = content;
+ newel->next = NULL;
+ newel->prev = aList->last;
+ if (aList->first == NULL)
+ aList->first = newel;
+ else
+ aList->last->next = newel;
+ aList->last = newel;
+ ++(aList->count);
+ aList->size += size;
+}
+
+
+/**
+ * Append an item to a list.
+ * @param aList the list to which the item is to be added
+ * @param content the list item content itself
+ * @param size the size of the element
+ */
+void ListAppend(List* aList, void* content, size_t size)
+{
+ ListElement* newel = malloc(sizeof(ListElement));
+ ListAppendNoMalloc(aList, content, newel, size);
+}
+
+
+/**
+ * Insert an item to a list at a specific position.
+ * @param aList the list to which the item is to be added
+ * @param content the list item content itself
+ * @param size the size of the element
+ * @param index the position in the list. If NULL, this function is equivalent
+ * to ListAppend.
+ */
+void ListInsert(List* aList, void* content, size_t size, ListElement* index)
+{
+ ListElement* newel = malloc(sizeof(ListElement));
+
+ if ( index == NULL )
+ ListAppendNoMalloc(aList, content, newel, size);
+ else
+ {
+ newel->content = content;
+ newel->next = index;
+ newel->prev = index->prev;
+
+ index->prev = newel;
+ if ( newel->prev != NULL )
+ newel->prev->next = newel;
+ else
+ aList->first = newel;
+
+ ++(aList->count);
+ aList->size += size;
+ }
+}
+
+
+/**
+ * Finds an element in a list by comparing the content pointers, rather than the contents
+ * @param aList the list in which the search is to be conducted
+ * @param content pointer to the list item content itself
+ * @return the list item found, or NULL
+ */
+ListElement* ListFind(List* aList, void* content)
+{
+ return ListFindItem(aList, content, NULL);
+}
+
+
+/**
+ * Finds an element in a list by comparing the content or pointer to the content. A callback
+ * function is used to define the method of comparison for each element.
+ * @param aList the list in which the search is to be conducted
+ * @param content pointer to the content to look for
+ * @param callback pointer to a function which compares each element (NULL means compare by content pointer)
+ * @return the list element found, or NULL
+ */
+ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*))
+{
+ ListElement* rc = NULL;
+
+ if (aList->current != NULL && ((callback == NULL && aList->current->content == content) ||
+ (callback != NULL && callback(aList->current->content, content))))
+ rc = aList->current;
+ else
+ {
+ ListElement* current = NULL;
+
+ /* find the content */
+ while (ListNextElement(aList, &current) != NULL)
+ {
+ if (callback == NULL)
+ {
+ if (current->content == content)
+ {
+ rc = current;
+ break;
+ }
+ }
+ else
+ {
+ if (callback(current->content, content))
+ {
+ rc = current;
+ break;
+ }
+ }
+ }
+ if (rc != NULL)
+ aList->current = rc;
+ }
+ return rc;
+}
+
+
+/**
+ * Removes and optionally frees an element in a list by comparing the content.
+ * A callback function is used to define the method of comparison for each element.
+ * @param aList the list in which the search is to be conducted
+ * @param content pointer to the content to look for
+ * @param callback pointer to a function which compares each element
+ * @param freeContent boolean value to indicate whether the item found is to be freed
+ * @return 1=item removed, 0=item not removed
+ */
+static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent)
+{
+ ListElement* next = NULL;
+ ListElement* saved = aList->current;
+ int saveddeleted = 0;
+
+ if (!ListFindItem(aList, content, callback))
+ return 0; /* false, did not remove item */
+
+ if (aList->current->prev == NULL)
+ /* so this is the first element, and we have to update the "first" pointer */
+ aList->first = aList->current->next;
+ else
+ aList->current->prev->next = aList->current->next;
+
+ if (aList->current->next == NULL)
+ aList->last = aList->current->prev;
+ else
+ aList->current->next->prev = aList->current->prev;
+
+ next = aList->current->next;
+ if (freeContent)
+ {
+ free(aList->current->content);
+ aList->current->content = NULL;
+ }
+ if (saved == aList->current)
+ saveddeleted = 1;
+ free(aList->current);
+ if (saveddeleted)
+ aList->current = next;
+ else
+ aList->current = saved;
+ --(aList->count);
+ return 1; /* successfully removed item */
+}
+
+
+/**
+ * Removes but does not free an item in a list by comparing the pointer to the content.
+ * @param aList the list in which the search is to be conducted
+ * @param content pointer to the content to look for
+ * @return 1=item removed, 0=item not removed
+ */
+int ListDetach(List* aList, void* content)
+{
+ return ListUnlink(aList, content, NULL, 0);
+}
+
+
+/**
+ * Removes and frees an item in a list by comparing the pointer to the content.
+ * @param aList the list from which the item is to be removed
+ * @param content pointer to the content to look for
+ * @return 1=item removed, 0=item not removed
+ */
+int ListRemove(List* aList, void* content)
+{
+ return ListUnlink(aList, content, NULL, 1);
+}
+
+
+/**
+ * Removes and frees an the first item in a list.
+ * @param aList the list from which the item is to be removed
+ * @return 1=item removed, 0=item not removed
+ */
+void* ListDetachHead(List* aList)
+{
+ void *content = NULL;
+ if (aList->count > 0)
+ {
+ ListElement* first = aList->first;
+ if (aList->current == first)
+ aList->current = first->next;
+ if (aList->last == first) /* i.e. no of items in list == 1 */
+ aList->last = NULL;
+ content = first->content;
+ aList->first = aList->first->next;
+ if (aList->first)
+ aList->first->prev = NULL;
+ free(first);
+ --(aList->count);
+ }
+ return content;
+}
+
+
+/**
+ * Removes and frees an the first item in a list.
+ * @param aList the list from which the item is to be removed
+ * @return 1=item removed, 0=item not removed
+ */
+int ListRemoveHead(List* aList)
+{
+ free(ListDetachHead(aList));
+ return 0;
+}
+
+
+/**
+ * Removes but does not free the last item in a list.
+ * @param aList the list from which the item is to be removed
+ * @return the last item removed (or NULL if none was)
+ */
+void* ListPopTail(List* aList)
+{
+ void* content = NULL;
+ if (aList->count > 0)
+ {
+ ListElement* last = aList->last;
+ if (aList->current == last)
+ aList->current = last->prev;
+ if (aList->first == last) /* i.e. no of items in list == 1 */
+ aList->first = NULL;
+ content = last->content;
+ aList->last = aList->last->prev;
+ if (aList->last)
+ aList->last->next = NULL;
+ free(last);
+ --(aList->count);
+ }
+ return content;
+}
+
+
+/**
+ * Removes but does not free an element in a list by comparing the content.
+ * A callback function is used to define the method of comparison for each element.
+ * @param aList the list in which the search is to be conducted
+ * @param content pointer to the content to look for
+ * @param callback pointer to a function which compares each element
+ * @return 1=item removed, 0=item not removed
+ */
+int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*))
+{ /* do not free the content */
+ return ListUnlink(aList, content, callback, 0);
+}
+
+
+/**
+ * Removes and frees an element in a list by comparing the content.
+ * A callback function is used to define the method of comparison for each element
+ * @param aList the list in which the search is to be conducted
+ * @param content pointer to the content to look for
+ * @param callback pointer to a function which compares each element
+ * @return 1=item removed, 0=item not removed
+ */
+int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*))
+{ /* remove from list and free the content */
+ return ListUnlink(aList, content, callback, 1);
+}
+
+
+/**
+ * Removes and frees all items in a list, leaving the list ready for new items.
+ * @param aList the list to which the operation is to be applied
+ */
+void ListEmpty(List* aList)
+{
+ while (aList->first != NULL)
+ {
+ ListElement* first = aList->first;
+ if (first->content != NULL)
+ {
+ free(first->content);
+ first->content = NULL;
+ }
+ aList->first = first->next;
+ free(first);
+ }
+ aList->count = 0;
+ aList->size = 0;
+ aList->current = aList->first = aList->last = NULL;
+}
+
+/**
+ * Removes and frees all items in a list, and frees the list itself
+ * @param aList the list to which the operation is to be applied
+ */
+void ListFree(List* aList)
+{
+ ListEmpty(aList);
+ free(aList);
+}
+
+
+/**
+ * Removes and but does not free all items in a list, and frees the list itself
+ * @param aList the list to which the operation is to be applied
+ */
+void ListFreeNoContent(List* aList)
+{
+ while (aList->first != NULL)
+ {
+ ListElement* first = aList->first;
+ aList->first = first->next;
+ free(first);
+ }
+ free(aList);
+}
+
+
+/**
+ * Forward iteration through a list
+ * @param aList the list to which the operation is to be applied
+ * @param pos pointer to the current position in the list. NULL means start from the beginning of the list
+ * This is updated on return to the same value as that returned from this function
+ * @return pointer to the current list element
+ */
+ListElement* ListNextElement(List* aList, ListElement** pos)
+{
+ return *pos = (*pos == NULL) ? aList->first : (*pos)->next;
+}
+
+
+/**
+ * Backward iteration through a list
+ * @param aList the list to which the operation is to be applied
+ * @param pos pointer to the current position in the list. NULL means start from the end of the list
+ * This is updated on return to the same value as that returned from this function
+ * @return pointer to the current list element
+ */
+ListElement* ListPrevElement(List* aList, ListElement** pos)
+{
+ return *pos = (*pos == NULL) ? aList->last : (*pos)->prev;
+}
+
+
+/**
+ * List callback function for comparing integers
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+int intcompare(void* a, void* b)
+{
+ return *((int*)a) == *((int*)b);
+}
+
+
+/**
+ * List callback function for comparing C strings
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+int stringcompare(void* a, void* b)
+{
+ return strcmp((char*)a, (char*)b) == 0;
+}
+
+
+#if defined(UNIT_TESTS)
+
+
+int main(int argc, char *argv[])
+{
+ int i, *ip, *todelete;
+ ListElement* current = NULL;
+ List* l = ListInitialize();
+ printf("List initialized\n");
+
+ for (i = 0; i < 10; i++)
+ {
+ ip = malloc(sizeof(int));
+ *ip = i;
+ ListAppend(l, (void*)ip, sizeof(int));
+ if (i==5)
+ todelete = ip;
+ printf("List element appended %d\n", *((int*)(l->last->content)));
+ }
+
+ printf("List contents:\n");
+ current = NULL;
+ while (ListNextElement(l, &current) != NULL)
+ printf("List element: %d\n", *((int*)(current->content)));
+
+ printf("List contents in reverse order:\n");
+ current = NULL;
+ while (ListPrevElement(l, &current) != NULL)
+ printf("List element: %d\n", *((int*)(current->content)));
+
+ /* if ListFindItem(l, *ip, intcompare)->content */
+
+ printf("List contents having deleted element %d:\n", *todelete);
+ ListRemove(l, todelete);
+ current = NULL;
+ while (ListNextElement(l, &current) != NULL)
+ printf("List element: %d\n", *((int*)(current->content)));
+
+ i = 9;
+ ListRemoveItem(l, &i, intcompare);
+ printf("List contents having deleted another element, %d, size now %d:\n", i, l->size);
+ current = NULL;
+ while (ListNextElement(l, &current) != NULL)
+ printf("List element: %d\n", *((int*)(current->content)));
+
+ ListFree(l);
+ printf("List freed\n");
+}
+
+#endif
+
+
+
+
+
diff --git a/tts-mqtt-test/src/paho-mqtt/LinkedList.h b/tts-mqtt-test/src/paho-mqtt/LinkedList.h
new file mode 100644
index 0000000..102a4fd
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/LinkedList.h
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - updates for the async client
+ * Ian Craggs - change size types from int to size_t
+ *******************************************************************************/
+
+#if !defined(LINKEDLIST_H)
+#define LINKEDLIST_H
+
+#include <stdlib.h> /* for size_t definition */
+
+/*BE
+defm defList(T)
+
+def T concat Item
+{
+ at 4
+ n32 ptr T concat Item suppress "next"
+ at 0
+ n32 ptr T concat Item suppress "prev"
+ at 8
+ n32 ptr T id2str(T)
+}
+
+def T concat List
+{
+ n32 ptr T concat Item suppress "first"
+ n32 ptr T concat Item suppress "last"
+ n32 ptr T concat Item suppress "current"
+ n32 dec "count"
+ n32 suppress "size"
+}
+endm
+
+defList(INT)
+defList(STRING)
+defList(TMP)
+
+BE*/
+
+/**
+ * Structure to hold all data for one list element
+ */
+typedef struct ListElementStruct
+{
+ struct ListElementStruct *prev, /**< pointer to previous list element */
+ *next; /**< pointer to next list element */
+ void* content; /**< pointer to element content */
+} ListElement;
+
+
+/**
+ * Structure to hold all data for one list
+ */
+typedef struct
+{
+ ListElement *first, /**< first element in the list */
+ *last, /**< last element in the list */
+ *current; /**< current element in the list, for iteration */
+ int count; /**< no of items */
+ size_t size; /**< heap storage used */
+} List;
+
+void ListZero(List*);
+List* ListInitialize(void);
+
+void ListAppend(List* aList, void* content, size_t size);
+void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size);
+void ListInsert(List* aList, void* content, size_t size, ListElement* index);
+
+int ListRemove(List* aList, void* content);
+int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*));
+void* ListDetachHead(List* aList);
+int ListRemoveHead(List* aList);
+void* ListPopTail(List* aList);
+
+int ListDetach(List* aList, void* content);
+int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*));
+
+void ListFree(List* aList);
+void ListEmpty(List* aList);
+void ListFreeNoContent(List* aList);
+
+ListElement* ListNextElement(List* aList, ListElement** pos);
+ListElement* ListPrevElement(List* aList, ListElement** pos);
+
+ListElement* ListFind(List* aList, void* content);
+ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*));
+
+int intcompare(void* a, void* b);
+int stringcompare(void* a, void* b);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/Log.c b/tts-mqtt-test/src/paho-mqtt/Log.c
new file mode 100644
index 0000000..1282dc8
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Log.c
@@ -0,0 +1,572 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - updates for the async client
+ * Ian Craggs - fix for bug #427028
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Logging and tracing module
+ *
+ *
+ */
+
+#include "../paho-mqtt/Log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/Messages.h"
+#include "../paho-mqtt/MQTTPacket.h"
+#include "../paho-mqtt/MQTTProtocol.h"
+#include "../paho-mqtt/MQTTProtocolClient.h"
+#include "../paho-mqtt/StackTrace.h"
+#include "../paho-mqtt/Thread.h"
+
+#if !defined(WIN32) && !defined(WIN64)
+#include <syslog.h>
+#include <sys/stat.h>
+#define GETTIMEOFDAY 1
+#else
+#define snprintf _snprintf
+#endif
+
+#if defined(GETTIMEOFDAY)
+ #include <sys/time.h>
+#else
+ #include <sys/timeb.h>
+#endif
+
+#if !defined(WIN32) && !defined(WIN64)
+/**
+ * _unlink mapping for linux
+ */
+#define _unlink unlink
+#endif
+
+
+#if !defined(min)
+#define min(A,B) ( (A) < (B) ? (A):(B))
+#endif
+
+trace_settings_type trace_settings =
+{
+ TRACE_MINIMUM,
+ 400,
+ INVALID_LEVEL
+};
+
+#define MAX_FUNCTION_NAME_LENGTH 256
+
+typedef struct
+{
+#if defined(GETTIMEOFDAY)
+ struct timeval ts;
+#else
+ struct timeb ts;
+#endif
+ int sametime_count;
+ int number;
+ int thread_id;
+ int depth;
+ char name[MAX_FUNCTION_NAME_LENGTH + 1];
+ int line;
+ int has_rc;
+ int rc;
+ enum LOG_LEVELS level;
+} traceEntry;
+
+static int start_index = -1,
+ next_index = 0;
+static traceEntry* trace_queue = NULL;
+static int trace_queue_size = 0;
+
+static FILE* trace_destination = NULL; /**< flag to indicate if trace is to be sent to a stream */
+static char* trace_destination_name = NULL; /**< the name of the trace file */
+static char* trace_destination_backup_name = NULL; /**< the name of the backup trace file */
+static int lines_written = 0; /**< number of lines written to the current output file */
+static int max_lines_per_file = 1000; /**< maximum number of lines to write to one trace file */
+static enum LOG_LEVELS trace_output_level = INVALID_LEVEL;
+static Log_traceCallback* trace_callback = NULL;
+static traceEntry* Log_pretrace(void);
+static char* Log_formatTraceEntry(traceEntry* cur_entry);
+static void Log_output(enum LOG_LEVELS log_level, const char *msg);
+static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry);
+static void Log_trace(enum LOG_LEVELS log_level, const char *buf);
+#if 0
+static FILE* Log_destToFile(const char *dest);
+static int Log_compareEntries(const char *entry1, const char *entry2);
+#endif
+
+static int sametime_count = 0;
+#if defined(GETTIMEOFDAY)
+struct timeval ts, last_ts;
+#else
+struct timeb ts, last_ts;
+#endif
+static char msg_buf[512];
+
+#if defined(WIN32) || defined(WIN64)
+mutex_type log_mutex;
+#else
+static pthread_mutex_t log_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type log_mutex = &log_mutex_store;
+#endif
+
+
+int Log_initialize(Log_nameValue* info)
+{
+ int rc = -1;
+ char* envval = NULL;
+
+ if ((trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries)) == NULL)
+ return rc;
+ trace_queue_size = trace_settings.max_trace_entries;
+
+ if ((envval = getenv("MQTT_C_CLIENT_TRACE")) != NULL && strlen(envval) > 0)
+ {
+ if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "w")) == NULL)
+ trace_destination = stdout;
+ else
+ {
+ trace_destination_name = malloc(strlen(envval) + 1);
+ strcpy(trace_destination_name, envval);
+ trace_destination_backup_name = malloc(strlen(envval) + 3);
+ sprintf(trace_destination_backup_name, "%s.0", trace_destination_name);
+ }
+ }
+ if ((envval = getenv("MQTT_C_CLIENT_TRACE_MAX_LINES")) != NULL && strlen(envval) > 0)
+ {
+ max_lines_per_file = atoi(envval);
+ if (max_lines_per_file <= 0)
+ max_lines_per_file = 1000;
+ }
+ if ((envval = getenv("MQTT_C_CLIENT_TRACE_LEVEL")) != NULL && strlen(envval) > 0)
+ {
+ if (strcmp(envval, "MAXIMUM") == 0 || strcmp(envval, "TRACE_MAXIMUM") == 0)
+ trace_settings.trace_level = TRACE_MAXIMUM;
+ else if (strcmp(envval, "MEDIUM") == 0 || strcmp(envval, "TRACE_MEDIUM") == 0)
+ trace_settings.trace_level = TRACE_MEDIUM;
+ else if (strcmp(envval, "MINIMUM") == 0 || strcmp(envval, "TRACE_MEDIUM") == 0)
+ trace_settings.trace_level = TRACE_MINIMUM;
+ else if (strcmp(envval, "PROTOCOL") == 0 || strcmp(envval, "TRACE_PROTOCOL") == 0)
+ trace_output_level = TRACE_PROTOCOL;
+ else if (strcmp(envval, "ERROR") == 0 || strcmp(envval, "TRACE_ERROR") == 0)
+ trace_output_level = LOG_ERROR;
+ }
+ Log_output(TRACE_MINIMUM, "=========================================================");
+ Log_output(TRACE_MINIMUM, " Trace Output");
+ if (info)
+ {
+ while (info->name)
+ {
+ snprintf(msg_buf, sizeof(msg_buf), "%s: %s", info->name, info->value);
+ Log_output(TRACE_MINIMUM, msg_buf);
+ info++;
+ }
+ }
+#if !defined(WIN32) && !defined(WIN64)
+ struct stat buf;
+ if (stat("/proc/version", &buf) != -1)
+ {
+ FILE* vfile;
+
+ if ((vfile = fopen("/proc/version", "r")) != NULL)
+ {
+ int len;
+
+ strcpy(msg_buf, "/proc/version: ");
+ len = strlen(msg_buf);
+ if (fgets(&msg_buf[len], sizeof(msg_buf) - len, vfile))
+ Log_output(TRACE_MINIMUM, msg_buf);
+ fclose(vfile);
+ }
+ }
+#endif
+ Log_output(TRACE_MINIMUM, "=========================================================");
+
+ return rc;
+}
+
+
+void Log_setTraceCallback(Log_traceCallback* callback)
+{
+ trace_callback = callback;
+}
+
+
+void Log_setTraceLevel(enum LOG_LEVELS level)
+{
+ if (level < TRACE_MINIMUM) /* the lowest we can go is TRACE_MINIMUM*/
+ trace_settings.trace_level = level;
+ trace_output_level = level;
+}
+
+
+void Log_terminate(void)
+{
+ free(trace_queue);
+ trace_queue = NULL;
+ trace_queue_size = 0;
+ if (trace_destination)
+ {
+ if (trace_destination != stdout)
+ fclose(trace_destination);
+ trace_destination = NULL;
+ }
+ if (trace_destination_name) {
+ free(trace_destination_name);
+ trace_destination_name = NULL;
+ }
+ if (trace_destination_backup_name) {
+ free(trace_destination_backup_name);
+ trace_destination_backup_name = NULL;
+ }
+ start_index = -1;
+ next_index = 0;
+ trace_output_level = INVALID_LEVEL;
+ sametime_count = 0;
+}
+
+
+static traceEntry* Log_pretrace(void)
+{
+ traceEntry *cur_entry = NULL;
+
+ /* calling ftime/gettimeofday seems to be comparatively expensive, so we need to limit its use */
+ if (++sametime_count % 20 == 0)
+ {
+#if defined(GETTIMEOFDAY)
+ gettimeofday(&ts, NULL);
+ if (ts.tv_sec != last_ts.tv_sec || ts.tv_usec != last_ts.tv_usec)
+#else
+ ftime(&ts);
+ if (ts.time != last_ts.time || ts.millitm != last_ts.millitm)
+#endif
+ {
+ sametime_count = 0;
+ last_ts = ts;
+ }
+ }
+
+ if (trace_queue_size != trace_settings.max_trace_entries)
+ {
+ traceEntry* new_trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries);
+
+ memcpy(new_trace_queue, trace_queue, min(trace_queue_size, trace_settings.max_trace_entries) * sizeof(traceEntry));
+ free(trace_queue);
+ trace_queue = new_trace_queue;
+ trace_queue_size = trace_settings.max_trace_entries;
+
+ if (start_index > trace_settings.max_trace_entries + 1 ||
+ next_index > trace_settings.max_trace_entries + 1)
+ {
+ start_index = -1;
+ next_index = 0;
+ }
+ }
+
+ /* add to trace buffer */
+ cur_entry = &trace_queue[next_index];
+ if (next_index == start_index) /* means the buffer is full */
+ {
+ if (++start_index == trace_settings.max_trace_entries)
+ start_index = 0;
+ } else if (start_index == -1)
+ start_index = 0;
+ if (++next_index == trace_settings.max_trace_entries)
+ next_index = 0;
+
+ return cur_entry;
+}
+
+static char* Log_formatTraceEntry(traceEntry* cur_entry)
+{
+ struct tm *timeinfo;
+ int buf_pos = 31;
+
+#if defined(GETTIMEOFDAY)
+ timeinfo = localtime((time_t *)&cur_entry->ts.tv_sec);
+#else
+ timeinfo = localtime(&cur_entry->ts.time);
+#endif
+ strftime(&msg_buf[7], 80, "%Y%m%d %H%M%S ", timeinfo);
+#if defined(GETTIMEOFDAY)
+ sprintf(&msg_buf[22], ".%.3lu ", cur_entry->ts.tv_usec / 1000L);
+#else
+ sprintf(&msg_buf[22], ".%.3hu ", cur_entry->ts.millitm);
+#endif
+ buf_pos = 27;
+
+ sprintf(msg_buf, "(%.4d)", cur_entry->sametime_count);
+ msg_buf[6] = ' ';
+
+ if (cur_entry->has_rc == 2)
+ strncpy(&msg_buf[buf_pos], cur_entry->name, sizeof(msg_buf)-buf_pos);
+ else
+ {
+ const char *format = Messages_get(cur_entry->number, cur_entry->level);
+ if (cur_entry->has_rc == 1)
+ snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
+ cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line, cur_entry->rc);
+ else
+ snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
+ cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line);
+ }
+ return msg_buf;
+}
+
+
+static void Log_output(enum LOG_LEVELS log_level, const char *msg)
+{
+ if (trace_destination)
+ {
+ fprintf(trace_destination, "%s\n", msg);
+
+ if (trace_destination != stdout && ++lines_written >= max_lines_per_file)
+ {
+
+ fclose(trace_destination);
+ _unlink(trace_destination_backup_name); /* remove any old backup trace file */
+ rename(trace_destination_name, trace_destination_backup_name); /* rename recently closed to backup */
+ trace_destination = fopen(trace_destination_name, "w"); /* open new trace file */
+ if (trace_destination == NULL)
+ trace_destination = stdout;
+ lines_written = 0;
+ }
+ else
+ fflush(trace_destination);
+ }
+
+ if (trace_callback)
+ (*trace_callback)(log_level, msg);
+}
+
+
+static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry)
+{
+ if (((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
+ {
+ char* msg = NULL;
+
+ if (trace_destination || trace_callback)
+ msg = &Log_formatTraceEntry(cur_entry)[7];
+
+ Log_output(log_level, msg);
+ }
+}
+
+
+static void Log_trace(enum LOG_LEVELS log_level, const char *buf)
+{
+ traceEntry *cur_entry = NULL;
+
+ if (trace_queue == NULL)
+ return;
+
+ cur_entry = Log_pretrace();
+
+ memcpy(&(cur_entry->ts), &ts, sizeof(ts));
+ cur_entry->sametime_count = sametime_count;
+
+ cur_entry->has_rc = 2;
+ strncpy(cur_entry->name, buf, sizeof(cur_entry->name));
+ cur_entry->name[MAX_FUNCTION_NAME_LENGTH] = '\0';
+
+ Log_posttrace(log_level, cur_entry);
+}
+
+
+/**
+ * Log a message. If possible, all messages should be indexed by message number, and
+ * the use of the format string should be minimized or negated altogether. If format is
+ * provided, the message number is only used as a message label.
+ * @param log_level the log level of the message
+ * @param msgno the id of the message to use if the format string is NULL
+ * @param aFormat the printf format string to be used if the message id does not exist
+ * @param ... the printf inserts
+ */
+void Log(enum LOG_LEVELS log_level, int msgno, const char *format, ...)
+{
+ if (log_level >= trace_settings.trace_level)
+ {
+ const char *temp = NULL;
+ static char msg_buf[512];
+ va_list args;
+
+ /* we're using a static character buffer, so we need to make sure only one thread uses it at a time */
+ Thread_lock_mutex(log_mutex);
+ if (format == NULL && (temp = Messages_get(msgno, log_level)) != NULL)
+ format = temp;
+
+ va_start(args, format);
+ vsnprintf(msg_buf, sizeof(msg_buf), format, args);
+
+ Log_trace(log_level, msg_buf);
+ va_end(args);
+ Thread_unlock_mutex(log_mutex);
+ }
+
+ /*if (log_level >= LOG_ERROR)
+ {
+ char* filename = NULL;
+ Log_recordFFDC(&msg_buf[7]);
+ }
+ */
+}
+
+
+/**
+ * The reason for this function is to make trace logging as fast as possible so that the
+ * function exit/entry history can be captured by default without unduly impacting
+ * performance. Therefore it must do as little as possible.
+ * @param log_level the log level of the message
+ * @param msgno the id of the message to use if the format string is NULL
+ * @param aFormat the printf format string to be used if the message id does not exist
+ * @param ... the printf inserts
+ */
+void Log_stackTrace(enum LOG_LEVELS log_level, int msgno, int thread_id, int current_depth, const char* name, int line, int* rc)
+{
+ traceEntry *cur_entry = NULL;
+
+ if (trace_queue == NULL)
+ return;
+
+ if (log_level < trace_settings.trace_level)
+ return;
+
+ Thread_lock_mutex(log_mutex);
+ cur_entry = Log_pretrace();
+
+ memcpy(&(cur_entry->ts), &ts, sizeof(ts));
+ cur_entry->sametime_count = sametime_count;
+ cur_entry->number = msgno;
+ cur_entry->thread_id = thread_id;
+ cur_entry->depth = current_depth;
+ strcpy(cur_entry->name, name);
+ cur_entry->level = log_level;
+ cur_entry->line = line;
+ if (rc == NULL)
+ cur_entry->has_rc = 0;
+ else
+ {
+ cur_entry->has_rc = 1;
+ cur_entry->rc = *rc;
+ }
+
+ Log_posttrace(log_level, cur_entry);
+ Thread_unlock_mutex(log_mutex);
+}
+
+
+#if 0
+static FILE* Log_destToFile(const char *dest)
+{
+ FILE* file = NULL;
+
+ if (strcmp(dest, "stdout") == 0)
+ file = stdout;
+ else if (strcmp(dest, "stderr") == 0)
+ file = stderr;
+ else
+ {
+ if (strstr(dest, "FFDC"))
+ file = fopen(dest, "ab");
+ else
+ file = fopen(dest, "wb");
+ }
+ return file;
+}
+
+
+static int Log_compareEntries(const char *entry1, const char *entry2)
+{
+ int comp = strncmp(&entry1[7], &entry2[7], 19);
+
+ /* if timestamps are equal, use the sequence numbers */
+ if (comp == 0)
+ comp = strncmp(&entry1[1], &entry2[1], 4);
+
+ return comp;
+}
+
+
+/**
+ * Write the contents of the stored trace to a stream
+ * @param dest string which contains a file name or the special strings stdout or stderr
+ */
+int Log_dumpTrace(char* dest)
+{
+ FILE* file = NULL;
+ ListElement* cur_trace_entry = NULL;
+ const int msgstart = 7;
+ int rc = -1;
+ int trace_queue_index = 0;
+
+ if ((file = Log_destToFile(dest)) == NULL)
+ {
+ Log(LOG_ERROR, 9, NULL, "trace", dest, "trace entries");
+ goto exit;
+ }
+
+ fprintf(file, "=========== Start of trace dump ==========\n");
+ /* Interleave the log and trace entries together appropriately */
+ ListNextElement(trace_buffer, &cur_trace_entry);
+ trace_queue_index = start_index;
+ if (trace_queue_index == -1)
+ trace_queue_index = next_index;
+ else
+ {
+ Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
+ if (trace_queue_index == trace_settings.max_trace_entries)
+ trace_queue_index = 0;
+ }
+ while (cur_trace_entry || trace_queue_index != next_index)
+ {
+ if (cur_trace_entry && trace_queue_index != -1)
+ { /* compare these timestamps */
+ if (Log_compareEntries((char*)cur_trace_entry->content, msg_buf) > 0)
+ cur_trace_entry = NULL;
+ }
+
+ if (cur_trace_entry)
+ {
+ fprintf(file, "%s\n", &((char*)(cur_trace_entry->content))[msgstart]);
+ ListNextElement(trace_buffer, &cur_trace_entry);
+ }
+ else
+ {
+ fprintf(file, "%s\n", &msg_buf[7]);
+ if (trace_queue_index != next_index)
+ {
+ Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
+ if (trace_queue_index == trace_settings.max_trace_entries)
+ trace_queue_index = 0;
+ }
+ }
+ }
+ fprintf(file, "========== End of trace dump ==========\n\n");
+ if (file != stdout && file != stderr && file != NULL)
+ fclose(file);
+ rc = 0;
+exit:
+ return rc;
+}
+#endif
+
+
diff --git a/tts-mqtt-test/src/paho-mqtt/Log.h b/tts-mqtt-test/src/paho-mqtt/Log.h
new file mode 100644
index 0000000..455beb6
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Log.h
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - updates for the async client
+ *******************************************************************************/
+
+#if !defined(LOG_H)
+#define LOG_H
+
+/*BE
+map LOG_LEVELS
+{
+ "TRACE_MAXIMUM" 1
+ "TRACE_MEDIUM" 2
+ "TRACE_MINIMUM" 3
+ "TRACE_PROTOCOL" 4
+
+ "ERROR" 5
+ "SEVERE" 6
+ "FATAL" 7
+}
+BE*/
+
+enum LOG_LEVELS {
+ INVALID_LEVEL = -1,
+ TRACE_MAXIMUM = 1,
+ TRACE_MEDIUM,
+ TRACE_MINIMUM,
+ TRACE_PROTOCOL,
+ LOG_ERROR,
+ LOG_SEVERE,
+ LOG_FATAL,
+};
+
+
+/*BE
+def trace_settings_type
+{
+ n32 map LOG_LEVELS "trace_level"
+ n32 dec "max_trace_entries"
+ n32 dec "trace_output_level"
+}
+BE*/
+typedef struct
+{
+ enum LOG_LEVELS trace_level; /**< trace level */
+ int max_trace_entries; /**< max no of entries in the trace buffer */
+ enum LOG_LEVELS trace_output_level; /**< trace level to output to destination */
+} trace_settings_type;
+
+extern trace_settings_type trace_settings;
+
+#define LOG_PROTOCOL TRACE_PROTOCOL
+#define TRACE_MAX TRACE_MAXIMUM
+#define TRACE_MIN TRACE_MINIMUM
+#define TRACE_MED TRACE_MEDIUM
+
+typedef struct
+{
+ const char* name;
+ const char* value;
+} Log_nameValue;
+
+int Log_initialize(Log_nameValue*);
+void Log_terminate(void);
+
+void Log(enum LOG_LEVELS, int, const char *, ...);
+void Log_stackTrace(enum LOG_LEVELS, int, int, int, const char*, int, int*);
+
+typedef void Log_traceCallback(enum LOG_LEVELS level, const char *message);
+void Log_setTraceCallback(Log_traceCallback* callback);
+void Log_setTraceLevel(enum LOG_LEVELS level);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTAsync.c b/tts-mqtt-test/src/paho-mqtt/MQTTAsync.c
new file mode 100644
index 0000000..5d686f1
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTAsync.c
@@ -0,0 +1,3331 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial implementation and documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL support
+ * Ian Craggs - multiple server connection support
+ * Ian Craggs - fix for bug 413429 - connectionLost not called
+ * Ian Craggs - fix for bug 415042 - using already freed structure
+ * Ian Craggs - fix for bug 419233 - mutexes not reporting errors
+ * Ian Craggs - fix for bug 420851
+ * Ian Craggs - fix for bug 432903 - queue persistence
+ * Ian Craggs - MQTT 3.1.1 support
+ * Rong Xiang, Ian Craggs - C++ compatibility
+ * Ian Craggs - fix for bug 442400: reconnecting after network cable unplugged
+ * Ian Craggs - fix for bug 444934 - incorrect free in freeCommand1
+ * Ian Craggs - fix for bug 445891 - assigning msgid is not thread safe
+ * Ian Craggs - fix for bug 465369 - longer latency than expected
+ * Ian Craggs - fix for bug 444103 - success/failure callbacks not invoked
+ * Ian Craggs - fix for bug 484363 - segfault in getReadySocket
+ * Ian Craggs - automatic reconnect and offline buffering (send while disconnected)
+ * Ian Craggs - fix for bug 472250
+ * Ian Craggs - fix for bug 486548
+ * Ian Craggs - SNI support
+ * Ian Craggs - auto reconnect timing fix #218
+ * Ian Craggs - fix for issue #190
+ * Ian Craggs - check for NULL SSL options #334
+ * Ian Craggs - allocate username/password buffers #431
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Asynchronous API implementation
+ *
+ */
+
+#define _GNU_SOURCE /* for pthread_mutexattr_settype */
+#include <stdlib.h>
+#include <string.h>
+#if !defined(WIN32) && !defined(WIN64)
+ #include <sys/time.h>
+#endif
+
+#if !defined(NO_PERSISTENCE)
+#include "../paho-mqtt/MQTTPersistence.h"
+#endif
+#include "../paho-mqtt/MQTTAsync.h"
+#include "../paho-mqtt/utf-8.h"
+#include "../paho-mqtt/MQTTProtocol.h"
+#include "../paho-mqtt/MQTTProtocolOut.h"
+#include "../paho-mqtt/Thread.h"
+#include "../paho-mqtt/SocketBuffer.h"
+#include "../paho-mqtt/StackTrace.h"
+#include "../paho-mqtt/Heap.h"
+#include "../paho-mqtt/OsWrapper.h"
+
+#define URI_TCP "tcp://"
+
+#include "../paho-mqtt/VersionInfo.h"
+
+const char *client_timestamp_eye = "MQTTAsyncV3_Timestamp " BUILD_TIMESTAMP;
+const char *client_version_eye = "MQTTAsyncV3_Version " CLIENT_VERSION;
+
+void MQTTAsync_global_init(MQTTAsync_init_options* inits)
+{
+#if defined(OPENSSL)
+ SSLSocket_handleOpensslInit(inits->do_openssl_init);
+#endif
+}
+
+#if !defined(min)
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+static ClientStates ClientState =
+{
+ CLIENT_VERSION, /* version */
+ NULL /* client list */
+};
+
+ClientStates* bstate = &ClientState;
+
+MQTTProtocol state;
+
+enum MQTTAsync_threadStates
+{
+ STOPPED, STARTING, RUNNING, STOPPING
+};
+
+enum MQTTAsync_threadStates sendThread_state = STOPPED;
+enum MQTTAsync_threadStates receiveThread_state = STOPPED;
+static thread_id_type sendThread_id = 0,
+ receiveThread_id = 0;
+
+#if defined(WIN32) || defined(WIN64)
+static mutex_type mqttasync_mutex = NULL;
+static mutex_type socket_mutex = NULL;
+static mutex_type mqttcommand_mutex = NULL;
+static sem_type send_sem = NULL;
+extern mutex_type stack_mutex;
+extern mutex_type heap_mutex;
+extern mutex_type log_mutex;
+BOOL APIENTRY DllMain(HANDLE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ Log(TRACE_MAX, -1, "DLL process attach");
+ if (mqttasync_mutex == NULL)
+ {
+ mqttasync_mutex = CreateMutex(NULL, 0, NULL);
+ mqttcommand_mutex = CreateMutex(NULL, 0, NULL);
+ send_sem = CreateEvent(
+ NULL, /* default security attributes */
+ FALSE, /* manual-reset event? */
+ FALSE, /* initial state is nonsignaled */
+ NULL /* object name */
+ );
+ stack_mutex = CreateMutex(NULL, 0, NULL);
+ heap_mutex = CreateMutex(NULL, 0, NULL);
+ log_mutex = CreateMutex(NULL, 0, NULL);
+ socket_mutex = CreateMutex(NULL, 0, NULL);
+ }
+ case DLL_THREAD_ATTACH:
+ Log(TRACE_MAX, -1, "DLL thread attach");
+ case DLL_THREAD_DETACH:
+ Log(TRACE_MAX, -1, "DLL thread detach");
+ case DLL_PROCESS_DETACH:
+ Log(TRACE_MAX, -1, "DLL process detach");
+ }
+ return TRUE;
+}
+#else
+static pthread_mutex_t mqttasync_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type mqttasync_mutex = &mqttasync_mutex_store;
+
+static pthread_mutex_t socket_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type socket_mutex = &socket_mutex_store;
+
+static pthread_mutex_t mqttcommand_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type mqttcommand_mutex = &mqttcommand_mutex_store;
+
+static cond_type_struct send_cond_store = { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
+static cond_type send_cond = &send_cond_store;
+
+void MQTTAsync_init(void)
+{
+ pthread_mutexattr_t attr;
+ int rc;
+
+ pthread_mutexattr_init(&attr);
+#if !defined(_WRS_KERNEL)
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#else
+ /* #warning "no pthread_mutexattr_settype" */
+#endif
+ if ((rc = pthread_mutex_init(mqttasync_mutex, &attr)) != 0)
+ printf("MQTTAsync: error %d initializing async_mutex\n", rc);
+ if ((rc = pthread_mutex_init(mqttcommand_mutex, &attr)) != 0)
+ printf("MQTTAsync: error %d initializing command_mutex\n", rc);
+ if ((rc = pthread_mutex_init(socket_mutex, &attr)) != 0)
+ printf("MQTTClient: error %d initializing socket_mutex\n", rc);
+
+ if ((rc = pthread_cond_init(&send_cond->cond, NULL)) != 0)
+ printf("MQTTAsync: error %d initializing send_cond cond\n", rc);
+ if ((rc = pthread_mutex_init(&send_cond->mutex, &attr)) != 0)
+ printf("MQTTAsync: error %d initializing send_cond mutex\n", rc);
+}
+
+#define WINAPI
+#endif
+
+static volatile int initialized = 0;
+static List* handles = NULL;
+static int tostop = 0;
+static List* commands = NULL;
+
+
+#if defined(WIN32) || defined(WIN64)
+#define START_TIME_TYPE DWORD
+START_TIME_TYPE MQTTAsync_start_clock(void)
+{
+ return GetTickCount();
+}
+#elif defined(AIX)
+#define START_TIME_TYPE struct timespec
+START_TIME_TYPE MQTTAsync_start_clock(void)
+{
+ static struct timespec start;
+ clock_gettime(CLOCK_REALTIME, &start);
+ return start;
+}
+#else
+#define START_TIME_TYPE struct timeval
+START_TIME_TYPE MQTTAsync_start_clock(void)
+{
+ static struct timeval start;
+ gettimeofday(&start, NULL);
+ return start;
+}
+#endif
+
+
+#if defined(WIN32) || defined(WIN64)
+long MQTTAsync_elapsed(DWORD milliseconds)
+{
+ return GetTickCount() - milliseconds;
+}
+#elif defined(AIX)
+#define assert(a)
+long MQTTAsync_elapsed(struct timespec start)
+{
+ struct timespec now, res;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ ntimersub(now, start, res);
+ return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L;
+}
+#else
+long MQTTAsync_elapsed(struct timeval start)
+{
+ struct timeval now, res;
+
+ gettimeofday(&now, NULL);
+ timersub(&now, &start, &res);
+ return (res.tv_sec)*1000 + (res.tv_usec)/1000;
+}
+#endif
+
+
+typedef struct
+{
+ MQTTAsync_message* msg;
+ char* topicName;
+ int topicLen;
+ unsigned int seqno; /* only used on restore */
+} qEntry;
+
+typedef struct
+{
+ int type;
+ MQTTAsync_onSuccess* onSuccess;
+ MQTTAsync_onFailure* onFailure;
+ MQTTAsync_token token;
+ void* context;
+ START_TIME_TYPE start_time;
+ union
+ {
+ struct
+ {
+ int count;
+ char** topics;
+ int* qoss;
+ } sub;
+ struct
+ {
+ int count;
+ char** topics;
+ } unsub;
+ struct
+ {
+ char* destinationName;
+ int payloadlen;
+ void* payload;
+ int qos;
+ int retained;
+ } pub;
+ struct
+ {
+ int internal;
+ int timeout;
+ } dis;
+ struct
+ {
+ int currentURI;
+ int MQTTVersion; /**< current MQTT version being used to connect */
+ } conn;
+ } details;
+} MQTTAsync_command;
+
+
+typedef struct MQTTAsync_struct
+{
+ char* serverURI;
+ int ssl;
+ Clients* c;
+
+ /* "Global", to the client, callback definitions */
+ MQTTAsync_connectionLost* cl;
+ MQTTAsync_messageArrived* ma;
+ MQTTAsync_deliveryComplete* dc;
+ void* context; /* the context to be associated with the main callbacks*/
+
+ MQTTAsync_connected* connected;
+ void* connected_context; /* the context to be associated with the connected callback*/
+
+ /* Each time connect is called, we store the options that were used. These are reused in
+ any call to reconnect, or an automatic reconnect attempt */
+ MQTTAsync_command connect; /* Connect operation properties */
+ MQTTAsync_command disconnect; /* Disconnect operation properties */
+ MQTTAsync_command* pending_write; /* Is there a socket write pending? */
+
+ List* responses;
+ unsigned int command_seqno;
+
+ MQTTPacket* pack;
+
+ /* added for offline buffering */
+ MQTTAsync_createOptions* createOptions;
+ int shouldBeConnected;
+
+ /* added for automatic reconnect */
+ int automaticReconnect;
+ int minRetryInterval;
+ int maxRetryInterval;
+ int serverURIcount;
+ char** serverURIs;
+ int connectTimeout;
+
+ int currentInterval;
+ START_TIME_TYPE lastConnectionFailedTime;
+ int retrying;
+ int reconnectNow;
+
+} MQTTAsyncs;
+
+
+typedef struct
+{
+ MQTTAsync_command command;
+ MQTTAsyncs* client;
+ unsigned int seqno; /* only used on restore */
+} MQTTAsync_queuedCommand;
+
+
+static int clientSockCompare(void* a, void* b);
+static void MQTTAsync_lock_mutex(mutex_type amutex);
+static void MQTTAsync_unlock_mutex(mutex_type amutex);
+static int MQTTAsync_checkConn(MQTTAsync_command* command, MQTTAsyncs* client);
+static void MQTTAsync_terminate(void);
+#if !defined(NO_PERSISTENCE)
+static int MQTTAsync_unpersistCommand(MQTTAsync_queuedCommand* qcmd);
+static int MQTTAsync_persistCommand(MQTTAsync_queuedCommand* qcmd);
+static MQTTAsync_queuedCommand* MQTTAsync_restoreCommand(char* buffer, int buflen);
+/*static void MQTTAsync_insertInOrder(List* list, void* content, int size);*/
+static int MQTTAsync_restoreCommands(MQTTAsyncs* client);
+#endif
+static int MQTTAsync_addCommand(MQTTAsync_queuedCommand* command, int command_size);
+static void MQTTAsync_startConnectRetry(MQTTAsyncs* m);
+static void MQTTAsync_checkDisconnect(MQTTAsync handle, MQTTAsync_command* command);
+static void MQTTProtocol_checkPendingWrites(void);
+static void MQTTAsync_freeServerURIs(MQTTAsyncs* m);
+static void MQTTAsync_freeCommand1(MQTTAsync_queuedCommand *command);
+static void MQTTAsync_freeCommand(MQTTAsync_queuedCommand *command);
+static void MQTTAsync_writeComplete(int socket, int rc);
+static int MQTTAsync_processCommand(void);
+static void MQTTAsync_checkTimeouts(void);
+static thread_return_type WINAPI MQTTAsync_sendThread(void* n);
+static void MQTTAsync_emptyMessageQueue(Clients* client);
+static void MQTTAsync_removeResponsesAndCommands(MQTTAsyncs* m);
+static int MQTTAsync_completeConnection(MQTTAsyncs* m, MQTTPacket* pack);
+static thread_return_type WINAPI MQTTAsync_receiveThread(void* n);
+static void MQTTAsync_stop(void);
+static void MQTTAsync_closeOnly(Clients* client);
+static void MQTTAsync_closeSession(Clients* client);
+static int clientStructCompare(void* a, void* b);
+static int MQTTAsync_cleanSession(Clients* client);
+static int MQTTAsync_deliverMessage(MQTTAsyncs* m, char* topicName, size_t topicLen, MQTTAsync_message* mm);
+static int MQTTAsync_disconnect1(MQTTAsync handle, const MQTTAsync_disconnectOptions* options, int internal);
+static int MQTTAsync_disconnect_internal(MQTTAsync handle, int timeout);
+static int cmdMessageIDCompare(void* a, void* b);
+static int MQTTAsync_assignMsgId(MQTTAsyncs* m);
+static int MQTTAsync_countBufferedMessages(MQTTAsyncs* m);
+static void MQTTAsync_retry(void);
+static int MQTTAsync_connecting(MQTTAsyncs* m);
+static MQTTPacket* MQTTAsync_cycle(int* sock, unsigned long timeout, int* rc);
+/*static int pubCompare(void* a, void* b);*/
+
+
+void MQTTAsync_sleep(long milliseconds)
+{
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ Sleep(milliseconds);
+#else
+ usleep(milliseconds*1000);
+#endif
+ FUNC_EXIT;
+}
+
+
+/**
+ * List callback function for comparing clients by socket
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+static int clientSockCompare(void* a, void* b)
+{
+ MQTTAsyncs* m = (MQTTAsyncs*)a;
+ return m->c->net.socket == *(int*)b;
+}
+
+
+static void MQTTAsync_lock_mutex(mutex_type amutex)
+{
+ int rc = Thread_lock_mutex(amutex);
+ if (rc != 0)
+ Log(LOG_ERROR, 0, "Error %s locking mutex", strerror(rc));
+}
+
+
+static void MQTTAsync_unlock_mutex(mutex_type amutex)
+{
+ int rc = Thread_unlock_mutex(amutex);
+ if (rc != 0)
+ Log(LOG_ERROR, 0, "Error %s unlocking mutex", strerror(rc));
+}
+
+
+/*
+ Check whether there are any more connect options. If not then we are finished
+ with connect attempts.
+*/
+static int MQTTAsync_checkConn(MQTTAsync_command* command, MQTTAsyncs* client)
+{
+ int rc;
+
+ FUNC_ENTRY;
+ rc = command->details.conn.currentURI + 1 < client->serverURIcount ||
+ (command->details.conn.MQTTVersion == 4 && client->c->MQTTVersion == MQTTVERSION_DEFAULT);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_createWithOptions(MQTTAsync* handle, const char* serverURI, const char* clientId,
+ int persistence_type, void* persistence_context, MQTTAsync_createOptions* options)
+{
+ int rc = 0;
+ MQTTAsyncs *m = NULL;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+
+ if (serverURI == NULL || clientId == NULL)
+ {
+ rc = MQTTASYNC_NULL_PARAMETER;
+ goto exit;
+ }
+
+ if (!UTF8_validateString(clientId))
+ {
+ rc = MQTTASYNC_BAD_UTF8_STRING;
+ goto exit;
+ }
+
+ if (strstr(serverURI, "://") != NULL)
+ {
+ if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) != 0
+#if defined(OPENSSL)
+ && strncmp(URI_SSL, serverURI, strlen(URI_SSL)) != 0
+
+#endif
+ )
+ {
+ rc = MQTTASYNC_BAD_PROTOCOL;
+ goto exit;
+ }
+ }
+
+ if (options && (strncmp(options->struct_id, "MQCO", 4) != 0 || options->struct_version != 0))
+ {
+ rc = MQTTASYNC_BAD_STRUCTURE;
+ goto exit;
+ }
+
+ if (!initialized)
+ {
+ #if defined(HEAP_H)
+ Heap_initialize();
+ #endif
+ Log_initialize((Log_nameValue*)MQTTAsync_getVersionInfo());
+ bstate->clients = ListInitialize();
+ Socket_outInitialize();
+ Socket_setWriteCompleteCallback(MQTTAsync_writeComplete);
+ handles = ListInitialize();
+ commands = ListInitialize();
+#if defined(OPENSSL)
+ SSLSocket_initialize();
+#endif
+ initialized = 1;
+ }
+ m = malloc(sizeof(MQTTAsyncs));
+ *handle = m;
+ memset(m, '\0', sizeof(MQTTAsyncs));
+ if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
+ serverURI += strlen(URI_TCP);
+#if defined(OPENSSL)
+ else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
+ {
+ serverURI += strlen(URI_SSL);
+ m->ssl = 1;
+ }
+#endif
+ m->serverURI = MQTTStrdup(serverURI);
+ m->responses = ListInitialize();
+ ListAppend(handles, m, sizeof(MQTTAsyncs));
+
+ m->c = malloc(sizeof(Clients));
+ memset(m->c, '\0', sizeof(Clients));
+ m->c->context = m;
+ m->c->outboundMsgs = ListInitialize();
+ m->c->inboundMsgs = ListInitialize();
+ m->c->messageQueue = ListInitialize();
+ m->c->clientID = MQTTStrdup(clientId);
+
+ m->shouldBeConnected = 0;
+ if (options)
+ {
+ m->createOptions = malloc(sizeof(MQTTAsync_createOptions));
+ memcpy(m->createOptions, options, sizeof(MQTTAsync_createOptions));
+ }
+
+#if !defined(NO_PERSISTENCE)
+ rc = MQTTPersistence_create(&(m->c->persistence), persistence_type, persistence_context);
+ if (rc == 0)
+ {
+ rc = MQTTPersistence_initialize(m->c, m->serverURI);
+ if (rc == 0)
+ {
+ MQTTAsync_restoreCommands(m);
+ MQTTPersistence_restoreMessageQueue(m->c);
+ }
+ }
+#endif
+ ListAppend(bstate->clients, m->c, sizeof(Clients) + 3*sizeof(List));
+
+exit:
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_create(MQTTAsync* handle, const char* serverURI, const char* clientId,
+ int persistence_type, void* persistence_context)
+{
+ return MQTTAsync_createWithOptions(handle, serverURI, clientId, persistence_type,
+ persistence_context, NULL);
+}
+
+
+static void MQTTAsync_terminate(void)
+{
+ FUNC_ENTRY;
+ MQTTAsync_stop();
+ if (initialized)
+ {
+ ListElement* elem = NULL;
+ ListFree(bstate->clients);
+ ListFree(handles);
+ while (ListNextElement(commands, &elem))
+ MQTTAsync_freeCommand1((MQTTAsync_queuedCommand*)(elem->content));
+ ListFree(commands);
+ handles = NULL;
+ Socket_outTerminate();
+#if defined(OPENSSL)
+ SSLSocket_terminate();
+#endif
+ #if defined(HEAP_H)
+ Heap_terminate();
+ #endif
+ Log_terminate();
+ initialized = 0;
+ }
+ FUNC_EXIT;
+}
+
+
+#if !defined(NO_PERSISTENCE)
+static int MQTTAsync_unpersistCommand(MQTTAsync_queuedCommand* qcmd)
+{
+ int rc = 0;
+ char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
+
+ FUNC_ENTRY;
+ sprintf(key, "%s%u", PERSISTENCE_COMMAND_KEY, qcmd->seqno);
+ if ((rc = qcmd->client->c->persistence->premove(qcmd->client->c->phandle, key)) != 0)
+ Log(LOG_ERROR, 0, "Error %d removing command from persistence", rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static int MQTTAsync_persistCommand(MQTTAsync_queuedCommand* qcmd)
+{
+ int rc = 0;
+ MQTTAsyncs* aclient = qcmd->client;
+ MQTTAsync_command* command = &qcmd->command;
+ int* lens = NULL;
+ void** bufs = NULL;
+ int bufindex = 0, i, nbufs = 0;
+ char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
+
+ FUNC_ENTRY;
+ switch (command->type)
+ {
+ case SUBSCRIBE:
+ nbufs = 3 + (command->details.sub.count * 2);
+
+ lens = (int*)malloc(nbufs * sizeof(int));
+ bufs = malloc(nbufs * sizeof(char *));
+
+ bufs[bufindex] = &command->type;
+ lens[bufindex++] = sizeof(command->type);
+
+ bufs[bufindex] = &command->token;
+ lens[bufindex++] = sizeof(command->token);
+
+ bufs[bufindex] = &command->details.sub.count;
+ lens[bufindex++] = sizeof(command->details.sub.count);
+
+ for (i = 0; i < command->details.sub.count; ++i)
+ {
+ bufs[bufindex] = command->details.sub.topics[i];
+ lens[bufindex++] = (int)strlen(command->details.sub.topics[i]) + 1;
+ bufs[bufindex] = &command->details.sub.qoss[i];
+ lens[bufindex++] = sizeof(command->details.sub.qoss[i]);
+ }
+ sprintf(key, "%s%d", PERSISTENCE_COMMAND_KEY, ++aclient->command_seqno);
+ break;
+
+ case UNSUBSCRIBE:
+ nbufs = 3 + command->details.unsub.count;
+
+ lens = (int*)malloc(nbufs * sizeof(int));
+ bufs = malloc(nbufs * sizeof(char *));
+
+ bufs[bufindex] = &command->type;
+ lens[bufindex++] = sizeof(command->type);
+
+ bufs[bufindex] = &command->token;
+ lens[bufindex++] = sizeof(command->token);
+
+ bufs[bufindex] = &command->details.unsub.count;
+ lens[bufindex++] = sizeof(command->details.unsub.count);
+
+ for (i = 0; i < command->details.unsub.count; ++i)
+ {
+ bufs[bufindex] = command->details.unsub.topics[i];
+ lens[bufindex++] = (int)strlen(command->details.unsub.topics[i]) + 1;
+ }
+ sprintf(key, "%s%d", PERSISTENCE_COMMAND_KEY, ++aclient->command_seqno);
+ break;
+
+ case PUBLISH:
+ nbufs = 7;
+
+ lens = (int*)malloc(nbufs * sizeof(int));
+ bufs = malloc(nbufs * sizeof(char *));
+
+ bufs[bufindex] = &command->type;
+ lens[bufindex++] = sizeof(command->type);
+
+ bufs[bufindex] = &command->token;
+ lens[bufindex++] = sizeof(command->token);
+
+ bufs[bufindex] = command->details.pub.destinationName;
+ lens[bufindex++] = (int)strlen(command->details.pub.destinationName) + 1;
+
+ bufs[bufindex] = &command->details.pub.payloadlen;
+ lens[bufindex++] = sizeof(command->details.pub.payloadlen);
+
+ bufs[bufindex] = command->details.pub.payload;
+ lens[bufindex++] = command->details.pub.payloadlen;
+
+ bufs[bufindex] = &command->details.pub.qos;
+ lens[bufindex++] = sizeof(command->details.pub.qos);
+
+ bufs[bufindex] = &command->details.pub.retained;
+ lens[bufindex++] = sizeof(command->details.pub.retained);
+
+ sprintf(key, "%s%d", PERSISTENCE_COMMAND_KEY, ++aclient->command_seqno);
+ break;
+ }
+ if (nbufs > 0)
+ {
+ if ((rc = aclient->c->persistence->pput(aclient->c->phandle, key, nbufs, (char**)bufs, lens)) != 0)
+ Log(LOG_ERROR, 0, "Error persisting command, rc %d", rc);
+ qcmd->seqno = aclient->command_seqno;
+ }
+ if (lens)
+ free(lens);
+ if (bufs)
+ free(bufs);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static MQTTAsync_queuedCommand* MQTTAsync_restoreCommand(char* buffer, int buflen)
+{
+ MQTTAsync_command* command = NULL;
+ MQTTAsync_queuedCommand* qcommand = NULL;
+ char* ptr = buffer;
+ int i;
+ size_t data_size;
+
+ FUNC_ENTRY;
+ qcommand = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(qcommand, '\0', sizeof(MQTTAsync_queuedCommand));
+ command = &qcommand->command;
+
+ command->type = *(int*)ptr;
+ ptr += sizeof(int);
+
+ command->token = *(MQTTAsync_token*)ptr;
+ ptr += sizeof(MQTTAsync_token);
+
+ switch (command->type)
+ {
+ case SUBSCRIBE:
+ command->details.sub.count = *(int*)ptr;
+ ptr += sizeof(int);
+
+ if (command->details.sub.count > 0)
+ {
+ command->details.sub.topics = (char **)malloc(sizeof(char *) * command->details.sub.count);
+ command->details.sub.qoss = (int *)malloc(sizeof(int) * command->details.sub.count);
+ }
+
+ for (i = 0; i < command->details.sub.count; ++i)
+ {
+ data_size = strlen(ptr) + 1;
+
+ command->details.sub.topics[i] = malloc(data_size);
+ strcpy(command->details.sub.topics[i], ptr);
+ ptr += data_size;
+
+ command->details.sub.qoss[i] = *(int*)ptr;
+ ptr += sizeof(int);
+ }
+ break;
+
+ case UNSUBSCRIBE:
+ command->details.unsub.count = *(int*)ptr;
+ ptr += sizeof(int);
+
+ if (command->details.unsub.count > 0)
+ {
+ command->details.unsub.topics = (char **)malloc(sizeof(char *) * command->details.unsub.count);
+ }
+
+ for (i = 0; i < command->details.unsub.count; ++i)
+ {
+ data_size = strlen(ptr) + 1;
+
+ command->details.unsub.topics[i] = malloc(data_size);
+ strcpy(command->details.unsub.topics[i], ptr);
+ ptr += data_size;
+ }
+ break;
+
+ case PUBLISH:
+ data_size = strlen(ptr) + 1;
+ command->details.pub.destinationName = malloc(data_size);
+ strcpy(command->details.pub.destinationName, ptr);
+ ptr += data_size;
+
+ command->details.pub.payloadlen = *(int*)ptr;
+ ptr += sizeof(int);
+
+ data_size = command->details.pub.payloadlen;
+ command->details.pub.payload = malloc(data_size);
+ memcpy(command->details.pub.payload, ptr, data_size);
+ ptr += data_size;
+
+ command->details.pub.qos = *(int*)ptr;
+ ptr += sizeof(int);
+
+ command->details.pub.retained = *(int*)ptr;
+ ptr += sizeof(int);
+ break;
+
+ default:
+ free(qcommand);
+ qcommand = NULL;
+
+ }
+
+ FUNC_EXIT;
+ return qcommand;
+}
+
+/*
+static void MQTTAsync_insertInOrder(List* list, void* content, int size)
+{
+ ListElement* index = NULL;
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ while (ListNextElement(list, &current) != NULL && index == NULL)
+ {
+ if (((MQTTAsync_queuedCommand*)content)->seqno < ((MQTTAsync_queuedCommand*)current->content)->seqno)
+ index = current;
+ }
+
+ ListInsert(list, content, size, index);
+ FUNC_EXIT;
+}*/
+
+
+static int MQTTAsync_restoreCommands(MQTTAsyncs* client)
+{
+ int rc = 0;
+ char **msgkeys;
+ int nkeys;
+ int i = 0;
+ Clients* c = client->c;
+ int commands_restored = 0;
+
+ FUNC_ENTRY;
+ if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
+ {
+ while (rc == 0 && i < nkeys)
+ {
+ char *buffer = NULL;
+ int buflen;
+
+ if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) != 0)
+ {
+ ;
+ }
+ else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0)
+ {
+ MQTTAsync_queuedCommand* cmd = MQTTAsync_restoreCommand(buffer, buflen);
+
+ if (cmd)
+ {
+ cmd->client = client;
+ cmd->seqno = atoi(msgkeys[i]+2);
+ MQTTPersistence_insertInOrder(commands, cmd, sizeof(MQTTAsync_queuedCommand));
+ free(buffer);
+ client->command_seqno = max(client->command_seqno, cmd->seqno);
+ commands_restored++;
+ }
+ }
+ if (msgkeys[i])
+ free(msgkeys[i]);
+ i++;
+ }
+ if (msgkeys != NULL)
+ free(msgkeys);
+ }
+ Log(TRACE_MINIMUM, -1, "%d commands restored for client %s", commands_restored, c->clientID);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+#endif
+
+
+static int MQTTAsync_addCommand(MQTTAsync_queuedCommand* command, int command_size)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttcommand_mutex);
+ /* Don't set start time if the connect command is already in process #218 */
+ if ((command->command.type != CONNECT) || (command->client->c->connect_state == 0))
+ command->command.start_time = MQTTAsync_start_clock();
+ if (command->command.type == CONNECT ||
+ (command->command.type == DISCONNECT && command->command.details.dis.internal))
+ {
+ MQTTAsync_queuedCommand* head = NULL;
+
+ if (commands->first)
+ head = (MQTTAsync_queuedCommand*)(commands->first->content);
+
+ if (head != NULL && head->client == command->client && head->command.type == command->command.type)
+ MQTTAsync_freeCommand(command); /* ignore duplicate connect or disconnect command */
+ else
+ ListInsert(commands, command, command_size, commands->first); /* add to the head of the list */
+ }
+ else
+ {
+ ListAppend(commands, command, command_size);
+#if !defined(NO_PERSISTENCE)
+ if (command->client->c->persistence)
+ MQTTAsync_persistCommand(command);
+#endif
+ }
+ MQTTAsync_unlock_mutex(mqttcommand_mutex);
+#if !defined(WIN32) && !defined(WIN64)
+ rc = Thread_signal_cond(send_cond);
+ if (rc != 0)
+ Log(LOG_ERROR, 0, "Error %d from signal cond", rc);
+#else
+ if (!Thread_check_sem(send_sem))
+ Thread_post_sem(send_sem);
+#endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void MQTTAsync_startConnectRetry(MQTTAsyncs* m)
+{
+ if (m->automaticReconnect && m->shouldBeConnected)
+ {
+ m->lastConnectionFailedTime = MQTTAsync_start_clock();
+ if (m->retrying)
+ m->currentInterval = min(m->currentInterval * 2, m->maxRetryInterval);
+ else
+ {
+ m->currentInterval = m->minRetryInterval;
+ m->retrying = 1;
+ }
+ }
+}
+
+
+int MQTTAsync_reconnect(MQTTAsync handle)
+{
+ int rc = MQTTASYNC_FAILURE;
+ MQTTAsyncs* m = handle;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+
+ if (m->automaticReconnect)
+ {
+ if (m->shouldBeConnected)
+ {
+ m->reconnectNow = 1;
+ if (m->retrying == 0)
+ {
+ m->currentInterval = m->minRetryInterval;
+ m->retrying = 1;
+ }
+ rc = MQTTASYNC_SUCCESS;
+ }
+ }
+ else
+ {
+ /* to reconnect, put the connect command to the head of the command queue */
+ MQTTAsync_queuedCommand* conn = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
+ conn->client = m;
+ conn->command = m->connect;
+ /* make sure that the version attempts are restarted */
+ if (m->c->MQTTVersion == MQTTVERSION_DEFAULT)
+ conn->command.details.conn.MQTTVersion = 0;
+ MQTTAsync_addCommand(conn, sizeof(m->connect));
+ rc = MQTTASYNC_SUCCESS;
+ }
+
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void MQTTAsync_checkDisconnect(MQTTAsync handle, MQTTAsync_command* command)
+{
+ MQTTAsyncs* m = handle;
+
+ FUNC_ENTRY;
+ /* wait for all inflight message flows to finish, up to timeout */;
+ if (m->c->outboundMsgs->count == 0 || MQTTAsync_elapsed(command->start_time) >= command->details.dis.timeout)
+ {
+ int was_connected = m->c->connected;
+ MQTTAsync_closeSession(m->c);
+ if (command->details.dis.internal)
+ {
+ if (m->cl && was_connected)
+ {
+ Log(TRACE_MIN, -1, "Calling connectionLost for client %s", m->c->clientID);
+ (*(m->cl))(m->context, NULL);
+ }
+ MQTTAsync_startConnectRetry(m);
+ }
+ else if (command->onSuccess)
+ {
+ Log(TRACE_MIN, -1, "Calling disconnect complete for client %s", m->c->clientID);
+ (*(command->onSuccess))(command->context, NULL);
+ }
+ }
+ FUNC_EXIT;
+}
+
+/**
+ * Call Socket_noPendingWrites(int socket) with protection by socket_mutex, see https://github.com/eclipse/paho.mqtt.c/issues/385
+ */
+static int MQTTAsync_Socket_noPendingWrites(int socket)
+{
+ int rc;
+ Thread_lock_mutex(socket_mutex);
+ rc = Socket_noPendingWrites(socket);
+ Thread_unlock_mutex(socket_mutex);
+ return rc;
+}
+
+/**
+ * See if any pending writes have been completed, and cleanup if so.
+ * Cleaning up means removing any publication data that was stored because the write did
+ * not originally complete.
+ */
+static void MQTTProtocol_checkPendingWrites(void)
+{
+ FUNC_ENTRY;
+ if (state.pending_writes.count > 0)
+ {
+ ListElement* le = state.pending_writes.first;
+ while (le)
+ {
+ if (Socket_noPendingWrites(((pending_write*)(le->content))->socket))
+ {
+ MQTTProtocol_removePublication(((pending_write*)(le->content))->p);
+ state.pending_writes.current = le;
+ ListRemove(&(state.pending_writes), le->content); /* does NextElement itself */
+ le = state.pending_writes.current;
+ }
+ else
+ ListNextElement(&(state.pending_writes), &le);
+ }
+ }
+ FUNC_EXIT;
+}
+
+
+static void MQTTAsync_freeServerURIs(MQTTAsyncs* m)
+{
+ int i;
+
+ for (i = 0; i < m->serverURIcount; ++i)
+ free(m->serverURIs[i]);
+ m->serverURIcount = 0;
+ if (m->serverURIs)
+ free(m->serverURIs);
+ m->serverURIs = NULL;
+}
+
+
+static void MQTTAsync_freeCommand1(MQTTAsync_queuedCommand *command)
+{
+ if (command->command.type == SUBSCRIBE)
+ {
+ int i;
+
+ for (i = 0; i < command->command.details.sub.count; i++)
+ free(command->command.details.sub.topics[i]);
+
+ free(command->command.details.sub.topics);
+ command->command.details.sub.topics = NULL;
+ free(command->command.details.sub.qoss);
+ command->command.details.sub.qoss = NULL;
+ }
+ else if (command->command.type == UNSUBSCRIBE)
+ {
+ int i;
+
+ for (i = 0; i < command->command.details.unsub.count; i++)
+ free(command->command.details.unsub.topics[i]);
+
+ free(command->command.details.unsub.topics);
+ command->command.details.unsub.topics = NULL;
+ }
+ else if (command->command.type == PUBLISH)
+ {
+ /* qos 1 and 2 topics are freed in the protocol code when the flows are completed */
+ if (command->command.details.pub.destinationName)
+ free(command->command.details.pub.destinationName);
+ command->command.details.pub.destinationName = NULL;
+ free(command->command.details.pub.payload);
+ command->command.details.pub.payload = NULL;
+ }
+}
+
+static void MQTTAsync_freeCommand(MQTTAsync_queuedCommand *command)
+{
+ MQTTAsync_freeCommand1(command);
+ free(command);
+}
+
+
+static void MQTTAsync_writeComplete(int socket, int rc)
+{
+ ListElement* found = NULL;
+
+ FUNC_ENTRY;
+ /* a partial write is now complete for a socket - this will be on a publish*/
+
+ MQTTProtocol_checkPendingWrites();
+
+ /* find the client using this socket */
+ if ((found = ListFindItem(handles, &socket, clientSockCompare)) != NULL)
+ {
+ MQTTAsyncs* m = (MQTTAsyncs*)(found->content);
+
+ time(&(m->c->net.lastSent));
+
+ /* see if there is a pending write flagged */
+ if (m->pending_write)
+ {
+ ListElement* cur_response = NULL;
+ MQTTAsync_command* command = m->pending_write;
+ MQTTAsync_queuedCommand* com = NULL;
+
+ while (ListNextElement(m->responses, &cur_response))
+ {
+ com = (MQTTAsync_queuedCommand*)(cur_response->content);
+ if (com->client->pending_write == m->pending_write)
+ break;
+ }
+
+ if (cur_response) /* we found a response */
+ {
+ if (command->type == PUBLISH)
+ {
+ if (rc == 1 && command->onSuccess)
+ {
+ MQTTAsync_successData data;
+
+ data.token = command->token;
+ data.alt.pub.destinationName = command->details.pub.destinationName;
+ data.alt.pub.message.payload = command->details.pub.payload;
+ data.alt.pub.message.payloadlen = command->details.pub.payloadlen;
+ data.alt.pub.message.qos = command->details.pub.qos;
+ data.alt.pub.message.retained = command->details.pub.retained;
+ Log(TRACE_MIN, -1, "Calling publish success for client %s", m->c->clientID);
+ (*(command->onSuccess))(command->context, &data);
+ }
+ else if (rc == -1 && command->onFailure)
+ {
+ MQTTAsync_failureData data;
+
+ data.token = command->token;
+ data.code = rc;
+ data.message = NULL;
+ Log(TRACE_MIN, -1, "Calling publish failure for client %s", m->c->clientID);
+ (*(command->onFailure))(command->context, &data);
+ }
+ }
+ if (com)
+ {
+ ListDetach(m->responses, com);
+ MQTTAsync_freeCommand(com);
+ }
+ }
+ m->pending_write = NULL;
+ }
+ }
+ FUNC_EXIT;
+}
+
+
+static int MQTTAsync_processCommand(void)
+{
+ int rc = 0;
+ MQTTAsync_queuedCommand* command = NULL;
+ ListElement* cur_command = NULL;
+ List* ignored_clients = NULL;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ MQTTAsync_lock_mutex(mqttcommand_mutex);
+
+ /* only the first command in the list must be processed for any particular client, so if we skip
+ a command for a client, we must skip all following commands for that client. Use a list of
+ ignored clients to keep track
+ */
+ ignored_clients = ListInitialize();
+
+ /* don't try a command until there isn't a pending write for that client, and we are not connecting */
+ while (ListNextElement(commands, &cur_command))
+ {
+ MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(cur_command->content);
+
+ if (ListFind(ignored_clients, cmd->client))
+ continue;
+
+ if (cmd->command.type == CONNECT || cmd->command.type == DISCONNECT || (cmd->client->c->connected &&
+ cmd->client->c->connect_state == 0 && MQTTAsync_Socket_noPendingWrites(cmd->client->c->net.socket)))
+ {
+ if ((cmd->command.type == PUBLISH || cmd->command.type == SUBSCRIBE || cmd->command.type == UNSUBSCRIBE) &&
+ cmd->client->c->outboundMsgs->count >= MAX_MSG_ID - 1)
+ {
+ ; /* no more message ids available */
+ }
+ else
+ {
+ command = cmd;
+ break;
+ }
+ }
+ ListAppend(ignored_clients, cmd->client, sizeof(cmd->client));
+ }
+ ListFreeNoContent(ignored_clients);
+ if (command)
+ {
+ ListDetach(commands, command);
+#if !defined(NO_PERSISTENCE)
+ if (command->client->c->persistence)
+ MQTTAsync_unpersistCommand(command);
+#endif
+ }
+ MQTTAsync_unlock_mutex(mqttcommand_mutex);
+
+ if (!command)
+ goto exit; /* nothing to do */
+
+ if (command->command.type == CONNECT)
+ {
+ if (command->client->c->connect_state != 0 || command->client->c->connected)
+ rc = 0;
+ else
+ {
+ char* serverURI = command->client->serverURI;
+
+ if (command->client->serverURIcount > 0)
+ {
+ serverURI = command->client->serverURIs[command->command.details.conn.currentURI];
+
+ if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
+ serverURI += strlen(URI_TCP);
+#if defined(OPENSSL)
+ else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
+ {
+ serverURI += strlen(URI_SSL);
+ command->client->ssl = 1;
+ }
+#endif
+ }
+
+ if (command->client->c->MQTTVersion == MQTTVERSION_DEFAULT)
+ {
+ if (command->command.details.conn.MQTTVersion == 0)
+ command->command.details.conn.MQTTVersion = MQTTVERSION_3_1_1;
+ else if (command->command.details.conn.MQTTVersion == MQTTVERSION_3_1_1)
+ command->command.details.conn.MQTTVersion = MQTTVERSION_3_1;
+ }
+ else
+ command->command.details.conn.MQTTVersion = command->client->c->MQTTVersion;
+
+ Log(TRACE_MIN, -1, "Connecting to serverURI %s with MQTT version %d", serverURI, command->command.details.conn.MQTTVersion);
+#if defined(OPENSSL)
+ rc = MQTTProtocol_connect(serverURI, command->client->c, command->client->ssl, command->command.details.conn.MQTTVersion);
+#else
+ rc = MQTTProtocol_connect(serverURI, command->client->c, command->command.details.conn.MQTTVersion);
+#endif
+ if (command->client->c->connect_state == 0)
+ rc = SOCKET_ERROR;
+
+ /* if the TCP connect is pending, then we must call select to determine when the connect has completed,
+ which is indicated by the socket being ready *either* for reading *or* writing. The next couple of lines
+ make sure we check for writeability as well as readability, otherwise we wait around longer than we need to
+ in Socket_getReadySocket() */
+ if (rc == EINPROGRESS)
+ Socket_addPendingWrite(command->client->c->net.socket);
+ }
+ }
+ else if (command->command.type == SUBSCRIBE)
+ {
+ List* topics = ListInitialize();
+ List* qoss = ListInitialize();
+ int i;
+
+ for (i = 0; i < command->command.details.sub.count; i++)
+ {
+ ListAppend(topics, command->command.details.sub.topics[i], strlen(command->command.details.sub.topics[i]));
+ ListAppend(qoss, &command->command.details.sub.qoss[i], sizeof(int));
+ }
+ rc = MQTTProtocol_subscribe(command->client->c, topics, qoss, command->command.token);
+ ListFreeNoContent(topics);
+ ListFreeNoContent(qoss);
+ }
+ else if (command->command.type == UNSUBSCRIBE)
+ {
+ List* topics = ListInitialize();
+ int i;
+
+ for (i = 0; i < command->command.details.unsub.count; i++)
+ ListAppend(topics, command->command.details.unsub.topics[i], strlen(command->command.details.unsub.topics[i]));
+
+ rc = MQTTProtocol_unsubscribe(command->client->c, topics, command->command.token);
+ ListFreeNoContent(topics);
+ }
+ else if (command->command.type == PUBLISH)
+ {
+ Messages* msg = NULL;
+ Publish* p = NULL;
+
+ p = malloc(sizeof(Publish));
+
+ p->payload = command->command.details.pub.payload;
+ p->payloadlen = command->command.details.pub.payloadlen;
+ p->topic = command->command.details.pub.destinationName;
+ p->msgId = command->command.token;
+
+ rc = MQTTProtocol_startPublish(command->client->c, p, command->command.details.pub.qos, command->command.details.pub.retained, &msg);
+
+ if (command->command.details.pub.qos == 0)
+ {
+ if (rc == TCPSOCKET_COMPLETE)
+ {
+ if (command->command.onSuccess)
+ {
+ MQTTAsync_successData data;
+
+ data.token = command->command.token;
+ data.alt.pub.destinationName = command->command.details.pub.destinationName;
+ data.alt.pub.message.payload = command->command.details.pub.payload;
+ data.alt.pub.message.payloadlen = command->command.details.pub.payloadlen;
+ data.alt.pub.message.qos = command->command.details.pub.qos;
+ data.alt.pub.message.retained = command->command.details.pub.retained;
+ Log(TRACE_MIN, -1, "Calling publish success for client %s", command->client->c->clientID);
+ (*(command->command.onSuccess))(command->command.context, &data);
+ }
+ }
+ else
+ {
+ if (rc != SOCKET_ERROR)
+ command->command.details.pub.destinationName = NULL; /* this will be freed by the protocol code */
+ command->client->pending_write = &command->command;
+ }
+ }
+ else
+ command->command.details.pub.destinationName = NULL; /* this will be freed by the protocol code */
+ free(p); /* should this be done if the write isn't complete? */
+ }
+ else if (command->command.type == DISCONNECT)
+ {
+ if (command->client->c->connect_state != 0 || command->client->c->connected != 0)
+ {
+ if (command->client->c->connect_state != 0)
+ {
+ command->client->c->connect_state = -2;
+ if (command->client->connect.onFailure)
+ {
+ MQTTAsync_failureData data;
+
+ data.token = 0;
+ data.code = -2;
+ data.message = NULL;
+ Log(TRACE_MIN, -1, "Calling connect failure for client %s", command->client->c->clientID);
+ (*(command->client->connect.onFailure))(command->client->connect.context, &data);
+ }
+ }
+ MQTTAsync_checkDisconnect(command->client, &command->command);
+ }
+ }
+
+ if (command->command.type == CONNECT && rc != SOCKET_ERROR && rc != MQTTASYNC_PERSISTENCE_ERROR)
+ {
+ command->client->connect = command->command;
+ MQTTAsync_freeCommand(command);
+ }
+ else if (command->command.type == DISCONNECT)
+ {
+ command->client->disconnect = command->command;
+ MQTTAsync_freeCommand(command);
+ }
+ else if (command->command.type == PUBLISH && command->command.details.pub.qos == 0 &&
+ rc != SOCKET_ERROR && rc != MQTTASYNC_PERSISTENCE_ERROR)
+ {
+ if (rc == TCPSOCKET_INTERRUPTED)
+ ListAppend(command->client->responses, command, sizeof(command));
+ else
+ MQTTAsync_freeCommand(command);
+ }
+ else if (rc == SOCKET_ERROR || rc == MQTTASYNC_PERSISTENCE_ERROR)
+ {
+ if (command->command.type == CONNECT)
+ {
+ MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
+ MQTTAsync_disconnect(command->client, &opts); /* not "internal" because we don't want to call connection lost */
+ command->client->shouldBeConnected = 1; /* as above call is not "internal" we need to reset this */
+ }
+ else
+ MQTTAsync_disconnect_internal(command->client, 0);
+
+ if (command->command.type == CONNECT
+ && MQTTAsync_checkConn(&command->command, command->client))
+ {
+ Log(TRACE_MIN, -1, "Connect failed, more to try");
+
+ if (command->client->c->MQTTVersion == MQTTVERSION_DEFAULT)
+ {
+ if (command->command.details.conn.MQTTVersion == MQTTVERSION_3_1)
+ {
+ command->command.details.conn.currentURI++;
+ command->command.details.conn.MQTTVersion = MQTTVERSION_DEFAULT;
+ }
+ } else
+ command->command.details.conn.currentURI++;
+
+ /* put the connect command back to the head of the command queue, using the next serverURI */
+ rc = MQTTAsync_addCommand(command,
+ sizeof(command->command.details.conn));
+ } else
+ {
+ if (command->command.onFailure)
+ {
+ Log(TRACE_MIN, -1, "Calling command failure for client %s", command->client->c->clientID);
+ (*(command->command.onFailure))(command->command.context, NULL);
+ }
+ if (command->command.type == CONNECT)
+ {
+ command->client->connect = command->command;
+ MQTTAsync_startConnectRetry(command->client);
+ }
+ MQTTAsync_freeCommand(command); /* free up the command if necessary */
+ }
+ }
+ else /* put the command into a waiting for response queue for each client, indexed by msgid */
+ ListAppend(command->client->responses, command, sizeof(command));
+
+exit:
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ rc = (command != NULL);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void nextOrClose(MQTTAsyncs* m, int rc, char* message)
+{
+ if (MQTTAsync_checkConn(&m->connect, m))
+ {
+ MQTTAsync_queuedCommand* conn;
+
+ MQTTAsync_closeOnly(m->c);
+ /* put the connect command back to the head of the command queue, using the next serverURI */
+ conn = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
+ conn->client = m;
+ conn->command = m->connect;
+ Log(TRACE_MIN, -1, "Connect failed, more to try");
+
+ if (conn->client->c->MQTTVersion == MQTTVERSION_DEFAULT)
+ {
+ if (conn->command.details.conn.MQTTVersion == MQTTVERSION_3_1)
+ {
+ conn->command.details.conn.currentURI++;
+ conn->command.details.conn.MQTTVersion = MQTTVERSION_DEFAULT;
+ }
+ }
+ else
+ conn->command.details.conn.currentURI++;
+
+ MQTTAsync_addCommand(conn, sizeof(m->connect));
+ }
+ else
+ {
+ MQTTAsync_closeSession(m->c);
+ if (m->connect.onFailure)
+ {
+ MQTTAsync_failureData data;
+
+ data.token = 0;
+ data.code = rc;
+ data.message = message;
+ Log(TRACE_MIN, -1, "Calling connect failure for client %s", m->c->clientID);
+ (*(m->connect.onFailure))(m->connect.context, &data);
+ }
+ MQTTAsync_startConnectRetry(m);
+ }
+}
+
+
+static void MQTTAsync_checkTimeouts(void)
+{
+ ListElement* current = NULL;
+ static time_t last = 0L;
+ time_t now;
+
+ FUNC_ENTRY;
+ time(&(now));
+ if (difftime(now, last) < 3)
+ goto exit;
+
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ last = now;
+ while (ListNextElement(handles, &current)) /* for each client */
+ {
+ ListElement* cur_response = NULL;
+ int i = 0,
+ timed_out_count = 0;
+
+ MQTTAsyncs* m = (MQTTAsyncs*)(current->content);
+
+ /* check disconnect timeout */
+ if (m->c->connect_state == -2)
+ MQTTAsync_checkDisconnect(m, &m->disconnect);
+
+ /* check connect timeout */
+ if (m->c->connect_state != 0 && MQTTAsync_elapsed(m->connect.start_time) > (m->connectTimeout * 1000))
+ {
+ nextOrClose(m, MQTTASYNC_FAILURE, "TCP connect timeout");
+ continue;
+ }
+
+ timed_out_count = 0;
+ /* check response timeouts */
+ while (ListNextElement(m->responses, &cur_response))
+ {
+ MQTTAsync_queuedCommand* com = (MQTTAsync_queuedCommand*)(cur_response->content);
+
+ if (1 /*MQTTAsync_elapsed(com->command.start_time) < 120000*/)
+ break; /* command has not timed out */
+ else
+ {
+ if (com->command.onFailure)
+ {
+ Log(TRACE_MIN, -1, "Calling %s failure for client %s",
+ MQTTPacket_name(com->command.type), m->c->clientID);
+ (*(com->command.onFailure))(com->command.context, NULL);
+ }
+ timed_out_count++;
+ }
+ }
+ for (i = 0; i < timed_out_count; ++i)
+ ListRemoveHead(m->responses); /* remove the first response in the list */
+
+ if (m->automaticReconnect && m->retrying)
+ {
+ if (m->reconnectNow || MQTTAsync_elapsed(m->lastConnectionFailedTime) > (m->currentInterval * 1000))
+ {
+ /* to reconnect put the connect command to the head of the command queue */
+ MQTTAsync_queuedCommand* conn = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
+ conn->client = m;
+ conn->command = m->connect;
+ /* make sure that the version attempts are restarted */
+ if (m->c->MQTTVersion == MQTTVERSION_DEFAULT)
+ conn->command.details.conn.MQTTVersion = 0;
+ Log(TRACE_MIN, -1, "Automatically attempting to reconnect");
+ MQTTAsync_addCommand(conn, sizeof(m->connect));
+ m->reconnectNow = 0;
+ }
+ }
+ }
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+exit:
+ FUNC_EXIT;
+}
+
+
+static thread_return_type WINAPI MQTTAsync_sendThread(void* n)
+{
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ sendThread_state = RUNNING;
+ sendThread_id = Thread_getid();
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ while (!tostop)
+ {
+ int rc;
+
+ while (commands->count > 0)
+ {
+ if (MQTTAsync_processCommand() == 0)
+ break; /* no commands were processed, so go into a wait */
+ }
+#if !defined(WIN32) && !defined(WIN64)
+ if ((rc = Thread_wait_cond(send_cond, 1)) != 0 && rc != ETIMEDOUT)
+ Log(LOG_ERROR, -1, "Error %d waiting for condition variable", rc);
+#else
+ if ((rc = Thread_wait_sem(send_sem, 1000)) != 0 && rc != ETIMEDOUT)
+ Log(LOG_ERROR, -1, "Error %d waiting for semaphore", rc);
+#endif
+
+ MQTTAsync_checkTimeouts();
+ }
+ sendThread_state = STOPPING;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ sendThread_state = STOPPED;
+ sendThread_id = 0;
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT;
+ return 0;
+}
+
+
+static void MQTTAsync_emptyMessageQueue(Clients* client)
+{
+ FUNC_ENTRY;
+ /* empty message queue */
+ if (client->messageQueue->count > 0)
+ {
+ ListElement* current = NULL;
+ while (ListNextElement(client->messageQueue, &current))
+ {
+ qEntry* qe = (qEntry*)(current->content);
+ free(qe->topicName);
+ free(qe->msg->payload);
+ free(qe->msg);
+ }
+ ListEmpty(client->messageQueue);
+ }
+ FUNC_EXIT;
+}
+
+
+static void MQTTAsync_removeResponsesAndCommands(MQTTAsyncs* m)
+{
+ int count = 0;
+ ListElement* current = NULL;
+ ListElement *next = NULL;
+
+ FUNC_ENTRY;
+ if (m->responses)
+ {
+ ListElement* cur_response = NULL;
+
+ while (ListNextElement(m->responses, &cur_response))
+ {
+ MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(cur_response->content);
+
+ if (command->command.onFailure)
+ {
+ MQTTAsync_failureData data;
+
+ data.token = command->command.token;
+ data.code = MQTTASYNC_OPERATION_INCOMPLETE; /* interrupted return code */
+ data.message = NULL;
+
+ Log(TRACE_MIN, -1, "Calling %s failure for client %s",
+ MQTTPacket_name(command->command.type), m->c->clientID);
+ (*(command->command.onFailure))(command->command.context, &data);
+ }
+
+ MQTTAsync_freeCommand1(command);
+ count++;
+ }
+ }
+ ListEmpty(m->responses);
+ Log(TRACE_MINIMUM, -1, "%d responses removed for client %s", count, m->c->clientID);
+
+ /* remove commands in the command queue relating to this client */
+ count = 0;
+ current = ListNextElement(commands, &next);
+ ListNextElement(commands, &next);
+ while (current)
+ {
+ MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
+
+ if (command->client == m)
+ {
+ ListDetach(commands, command);
+
+ if (command->command.onFailure)
+ {
+ MQTTAsync_failureData data;
+
+ data.token = command->command.token;
+ data.code = MQTTASYNC_OPERATION_INCOMPLETE; /* interrupted return code */
+ data.message = NULL;
+
+ Log(TRACE_MIN, -1, "Calling %s failure for client %s",
+ MQTTPacket_name(command->command.type), m->c->clientID);
+ (*(command->command.onFailure))(command->command.context, &data);
+ }
+
+ MQTTAsync_freeCommand(command);
+ count++;
+ }
+ current = next;
+ ListNextElement(commands, &next);
+ }
+ Log(TRACE_MINIMUM, -1, "%d commands removed for client %s", count, m->c->clientID);
+ FUNC_EXIT;
+}
+
+
+void MQTTAsync_destroy(MQTTAsync* handle)
+{
+ MQTTAsyncs* m = *handle;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+
+ if (m == NULL)
+ goto exit;
+
+ MQTTAsync_closeSession(m->c);
+
+ MQTTAsync_removeResponsesAndCommands(m);
+ ListFree(m->responses);
+
+ if (m->c)
+ {
+ int saved_socket = m->c->net.socket;
+ char* saved_clientid = MQTTStrdup(m->c->clientID);
+#if !defined(NO_PERSISTENCE)
+ MQTTPersistence_close(m->c);
+#endif
+ MQTTAsync_emptyMessageQueue(m->c);
+ MQTTProtocol_freeClient(m->c);
+ if (!ListRemove(bstate->clients, m->c))
+ Log(LOG_ERROR, 0, NULL);
+ else
+ Log(TRACE_MIN, 1, NULL, saved_clientid, saved_socket);
+ free(saved_clientid);
+ }
+
+ if (m->serverURI)
+ free(m->serverURI);
+ if (m->createOptions)
+ free(m->createOptions);
+ MQTTAsync_freeServerURIs(m);
+ if (!ListRemove(handles, m))
+ Log(LOG_ERROR, -1, "free error");
+ *handle = NULL;
+ if (bstate->clients->count == 0)
+ MQTTAsync_terminate();
+
+exit:
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT;
+}
+
+
+void MQTTAsync_freeMessage(MQTTAsync_message** message)
+{
+ FUNC_ENTRY;
+ free((*message)->payload);
+ free(*message);
+ *message = NULL;
+ FUNC_EXIT;
+}
+
+
+void MQTTAsync_free(void* memory)
+{
+ FUNC_ENTRY;
+ free(memory);
+ FUNC_EXIT;
+}
+
+
+static int MQTTAsync_completeConnection(MQTTAsyncs* m, MQTTPacket* pack)
+{
+ int rc = MQTTASYNC_FAILURE;
+
+ FUNC_ENTRY;
+ if (m->c->connect_state == 3) /* MQTT connect sent - wait for CONNACK */
+ {
+ Connack* connack = (Connack*)pack;
+ Log(LOG_PROTOCOL, 1, NULL, m->c->net.socket, m->c->clientID, connack->rc);
+ if ((rc = connack->rc) == MQTTASYNC_SUCCESS)
+ {
+ m->retrying = 0;
+ m->c->connected = 1;
+ m->c->good = 1;
+ m->c->connect_state = 0;
+ if (m->c->cleansession)
+ rc = MQTTAsync_cleanSession(m->c);
+ if (m->c->outboundMsgs->count > 0)
+ {
+ ListElement* outcurrent = NULL;
+
+ while (ListNextElement(m->c->outboundMsgs, &outcurrent))
+ {
+ Messages* messages = (Messages*)(outcurrent->content);
+ messages->lastTouch = 0;
+ }
+ MQTTProtocol_retry((time_t)0, 1, 1);
+ if (m->c->connected != 1)
+ rc = MQTTASYNC_DISCONNECTED;
+ }
+ }
+ free(connack);
+ m->pack = NULL;
+#if !defined(WIN32) && !defined(WIN64)
+ Thread_signal_cond(send_cond);
+#else
+ if (!Thread_check_sem(send_sem))
+ Thread_post_sem(send_sem);
+#endif
+ }
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/* This is the thread function that handles the calling of callback functions if set */
+static thread_return_type WINAPI MQTTAsync_receiveThread(void* n)
+{
+ long timeout = 10L; /* first time in we have a small timeout. Gets things started more quickly */
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ receiveThread_state = RUNNING;
+ receiveThread_id = Thread_getid();
+ while (!tostop)
+ {
+ int rc = SOCKET_ERROR;
+ int sock = -1;
+ MQTTAsyncs* m = NULL;
+ MQTTPacket* pack = NULL;
+
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ pack = MQTTAsync_cycle(&sock, timeout, &rc);
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ if (tostop)
+ break;
+ timeout = 1000L;
+
+ if (sock == 0)
+ continue;
+ /* find client corresponding to socket */
+ if (ListFindItem(handles, &sock, clientSockCompare) == NULL)
+ {
+ Log(TRACE_MINIMUM, -1, "Could not find client corresponding to socket %d", sock);
+ /* Socket_close(sock); - removing socket in this case is not necessary (Bug 442400) */
+ continue;
+ }
+ m = (MQTTAsyncs*)(handles->current->content);
+ if (m == NULL)
+ {
+ Log(LOG_ERROR, -1, "Client structure was NULL for socket %d - removing socket", sock);
+ Socket_close(sock);
+ continue;
+ }
+ if (rc == SOCKET_ERROR)
+ {
+ Log(TRACE_MINIMUM, -1, "Error from MQTTAsync_cycle() - removing socket %d", sock);
+ if (m->c->connected == 1)
+ {
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ MQTTAsync_disconnect_internal(m, 0);
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ }
+ else if (m->c->connect_state != 0)
+ nextOrClose(m, rc, "socket error");
+ else /* calling disconnect_internal won't have any effect if we're already disconnected */
+ MQTTAsync_closeOnly(m->c);
+ }
+ else
+ {
+ if (m->c->messageQueue->count > 0)
+ {
+ qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
+ int topicLen = qe->topicLen;
+
+ if (strlen(qe->topicName) == topicLen)
+ topicLen = 0;
+
+ if (m->ma)
+ rc = MQTTAsync_deliverMessage(m, qe->topicName, topicLen, qe->msg);
+ else
+ rc = 1;
+
+ if (rc)
+ {
+#if !defined(NO_PERSISTENCE)
+ if (m->c->persistence)
+ MQTTPersistence_unpersistQueueEntry(m->c, (MQTTPersistence_qEntry*)qe);
+#endif
+ ListRemove(m->c->messageQueue, qe); /* qe is freed here */
+ }
+ else
+ Log(TRACE_MIN, -1, "False returned from messageArrived for client %s, message remains on queue",
+ m->c->clientID);
+ }
+ if (pack)
+ {
+ if (pack->header.bits.type == CONNACK)
+ {
+ int sessionPresent = ((Connack*)pack)->flags.bits.sessionPresent;
+ int rc = MQTTAsync_completeConnection(m, pack);
+
+ if (rc == MQTTASYNC_SUCCESS)
+ {
+ int onSuccess = 0;
+ if (m->serverURIcount > 0)
+ Log(TRACE_MIN, -1, "Connect succeeded to %s",
+ m->serverURIs[m->connect.details.conn.currentURI]);
+ onSuccess = (m->connect.onSuccess != NULL); /* save setting of onSuccess callback */
+ if (m->connect.onSuccess)
+ {
+ MQTTAsync_successData data;
+ memset(&data, '\0', sizeof(data));
+ Log(TRACE_MIN, -1, "Calling connect success for client %s", m->c->clientID);
+ if (m->serverURIcount > 0)
+ data.alt.connect.serverURI = m->serverURIs[m->connect.details.conn.currentURI];
+ else
+ data.alt.connect.serverURI = m->serverURI;
+ data.alt.connect.MQTTVersion = m->connect.details.conn.MQTTVersion;
+ data.alt.connect.sessionPresent = sessionPresent;
+ (*(m->connect.onSuccess))(m->connect.context, &data);
+ m->connect.onSuccess = NULL; /* don't accidentally call it again */
+ }
+ if (m->connected)
+ {
+ char* reason = (onSuccess) ? "connect onSuccess called" : "automatic reconnect";
+ Log(TRACE_MIN, -1, "Calling connected for client %s", m->c->clientID);
+ (*(m->connected))(m->connected_context, reason);
+ }
+ }
+ else
+ nextOrClose(m, rc, "CONNACK return code");
+ }
+ else if (pack->header.bits.type == SUBACK)
+ {
+ ListElement* current = NULL;
+
+ /* use the msgid to find the callback to be called */
+ while (ListNextElement(m->responses, &current))
+ {
+ MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
+ if (command->command.token == ((Suback*)pack)->msgId)
+ {
+ Suback* sub = (Suback*)pack;
+ if (!ListDetach(m->responses, command)) /* remove the response from the list */
+ Log(LOG_ERROR, -1, "Subscribe command not removed from command list");
+
+ /* Call the failure callback if there is one subscribe in the MQTT packet and
+ * the return code is 0x80 (failure). If the MQTT packet contains >1 subscription
+ * request, then we call onSuccess with the list of returned QoSs, which inelegantly,
+ * could include some failures, or worse, the whole list could have failed.
+ */
+ if (sub->qoss->count == 1 && *(int*)(sub->qoss->first->content) == MQTT_BAD_SUBSCRIBE)
+ {
+ if (command->command.onFailure)
+ {
+ MQTTAsync_failureData data;
+
+ data.token = command->command.token;
+ data.code = *(int*)(sub->qoss->first->content);
+ data.message = NULL;
+ Log(TRACE_MIN, -1, "Calling subscribe failure for client %s", m->c->clientID);
+ (*(command->command.onFailure))(command->command.context, &data);
+ }
+ }
+ else if (command->command.onSuccess)
+ {
+ MQTTAsync_successData data;
+ int* array = NULL;
+
+ if (sub->qoss->count == 1)
+ data.alt.qos = *(int*)(sub->qoss->first->content);
+ else if (sub->qoss->count > 1)
+ {
+ ListElement* cur_qos = NULL;
+ int* element = array = data.alt.qosList = malloc(sub->qoss->count * sizeof(int));
+ while (ListNextElement(sub->qoss, &cur_qos))
+ *element++ = *(int*)(cur_qos->content);
+ }
+ data.token = command->command.token;
+ Log(TRACE_MIN, -1, "Calling subscribe success for client %s", m->c->clientID);
+ (*(command->command.onSuccess))(command->command.context, &data);
+ if (array)
+ free(array);
+ }
+ MQTTAsync_freeCommand(command);
+ break;
+ }
+ }
+ rc = MQTTProtocol_handleSubacks(pack, m->c->net.socket);
+ }
+ else if (pack->header.bits.type == UNSUBACK)
+ {
+ ListElement* current = NULL;
+ int handleCalled = 0;
+
+ /* use the msgid to find the callback to be called */
+ while (ListNextElement(m->responses, &current))
+ {
+ MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
+ if (command->command.token == ((Unsuback*)pack)->msgId)
+ {
+ if (!ListDetach(m->responses, command)) /* remove the response from the list */
+ Log(LOG_ERROR, -1, "Unsubscribe command not removed from command list");
+ if (command->command.onSuccess)
+ {
+ rc = MQTTProtocol_handleUnsubacks(pack, m->c->net.socket);
+ handleCalled = 1;
+ Log(TRACE_MIN, -1, "Calling unsubscribe success for client %s", m->c->clientID);
+ (*(command->command.onSuccess))(command->command.context, NULL);
+ }
+ MQTTAsync_freeCommand(command);
+ break;
+ }
+ }
+ if (!handleCalled)
+ rc = MQTTProtocol_handleUnsubacks(pack, m->c->net.socket);
+ }
+ }
+ }
+ }
+ receiveThread_state = STOPPED;
+ receiveThread_id = 0;
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+#if !defined(WIN32) && !defined(WIN64)
+ if (sendThread_state != STOPPED)
+ Thread_signal_cond(send_cond);
+#else
+ if (sendThread_state != STOPPED && !Thread_check_sem(send_sem))
+ Thread_post_sem(send_sem);
+#endif
+ FUNC_EXIT;
+ return 0;
+}
+
+
+static void MQTTAsync_stop(void)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (sendThread_state != STOPPED || receiveThread_state != STOPPED)
+ {
+ int conn_count = 0;
+ ListElement* current = NULL;
+
+ if (handles != NULL)
+ {
+ /* find out how many handles are still connected */
+ while (ListNextElement(handles, &current))
+ {
+ if (((MQTTAsyncs*)(current->content))->c->connect_state > 0 ||
+ ((MQTTAsyncs*)(current->content))->c->connected)
+ ++conn_count;
+ }
+ }
+ Log(TRACE_MIN, -1, "Conn_count is %d", conn_count);
+ /* stop the background thread, if we are the last one to be using it */
+ if (conn_count == 0)
+ {
+ int count = 0;
+ tostop = 1;
+ while ((sendThread_state != STOPPED || receiveThread_state != STOPPED) && ++count < 100)
+ {
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ Log(TRACE_MIN, -1, "sleeping");
+ MQTTAsync_sleep(100L);
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ }
+ rc = 1;
+ tostop = 0;
+ }
+ }
+ FUNC_EXIT_RC(rc);
+}
+
+
+int MQTTAsync_setCallbacks(MQTTAsync handle, void* context,
+ MQTTAsync_connectionLost* cl,
+ MQTTAsync_messageArrived* ma,
+ MQTTAsync_deliveryComplete* dc)
+{
+ int rc = MQTTASYNC_SUCCESS;
+ MQTTAsyncs* m = handle;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+
+ if (m == NULL || ma == NULL || m->c->connect_state != 0)
+ rc = MQTTASYNC_FAILURE;
+ else
+ {
+ m->context = context;
+ m->cl = cl;
+ m->ma = ma;
+ m->dc = dc;
+ }
+
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_setConnected(MQTTAsync handle, void* context, MQTTAsync_connected* connected)
+{
+ int rc = MQTTASYNC_SUCCESS;
+ MQTTAsyncs* m = handle;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+
+ if (m == NULL || m->c->connect_state != 0)
+ rc = MQTTASYNC_FAILURE;
+ else
+ {
+ m->connected_context = context;
+ m->connected = connected;
+ }
+
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void MQTTAsync_closeOnly(Clients* client)
+{
+ FUNC_ENTRY;
+ client->good = 0;
+ client->ping_outstanding = 0;
+ if (client->net.socket > 0)
+ {
+ MQTTProtocol_checkPendingWrites();
+ if (client->connected && Socket_noPendingWrites(client->net.socket))
+ MQTTPacket_send_disconnect(&client->net, client->clientID);
+ Thread_lock_mutex(socket_mutex);
+#if defined(OPENSSL)
+ SSLSocket_close(&client->net);
+#endif
+ Socket_close(client->net.socket);
+ client->net.socket = 0;
+#if defined(OPENSSL)
+ client->net.ssl = NULL;
+#endif
+ Thread_unlock_mutex(socket_mutex);
+ }
+ client->connected = 0;
+ client->connect_state = 0;
+ FUNC_EXIT;
+}
+
+
+static void MQTTAsync_closeSession(Clients* client)
+{
+ FUNC_ENTRY;
+ MQTTAsync_closeOnly(client);
+
+ if (client->cleansession)
+ MQTTAsync_cleanSession(client);
+
+ FUNC_EXIT;
+}
+
+
+/**
+ * List callback function for comparing clients by client structure
+ * @param a Async structure
+ * @param b Client structure
+ * @return boolean indicating whether a and b are equal
+ */
+static int clientStructCompare(void* a, void* b)
+{
+ MQTTAsyncs* m = (MQTTAsyncs*)a;
+ return m->c == (Clients*)b;
+}
+
+
+static int MQTTAsync_cleanSession(Clients* client)
+{
+ int rc = 0;
+ ListElement* found = NULL;
+
+ FUNC_ENTRY;
+#if !defined(NO_PERSISTENCE)
+ rc = MQTTPersistence_clear(client);
+#endif
+ MQTTProtocol_emptyMessageList(client->inboundMsgs);
+ MQTTProtocol_emptyMessageList(client->outboundMsgs);
+ MQTTAsync_emptyMessageQueue(client);
+ client->msgID = 0;
+
+ if ((found = ListFindItem(handles, client, clientStructCompare)) != NULL)
+ {
+ MQTTAsyncs* m = (MQTTAsyncs*)(found->content);
+ MQTTAsync_removeResponsesAndCommands(m);
+ }
+ else
+ Log(LOG_ERROR, -1, "cleanSession: did not find client structure in handles list");
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static int MQTTAsync_deliverMessage(MQTTAsyncs* m, char* topicName, size_t topicLen, MQTTAsync_message* mm)
+{
+ int rc;
+
+ Log(TRACE_MIN, -1, "Calling messageArrived for client %s, queue depth %d",
+ m->c->clientID, m->c->messageQueue->count);
+ rc = (*(m->ma))(m->context, topicName, (int)topicLen, mm);
+ /* if 0 (false) is returned by the callback then it failed, so we don't remove the message from
+ * the queue, and it will be retried later. If 1 is returned then the message data may have been freed,
+ * so we must be careful how we use it.
+ */
+ return rc;
+}
+
+
+void Protocol_processPublication(Publish* publish, Clients* client)
+{
+ MQTTAsync_message* mm = NULL;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ mm = malloc(sizeof(MQTTAsync_message));
+
+ /* If the message is QoS 2, then we have already stored the incoming payload
+ * in an allocated buffer, so we don't need to copy again.
+ */
+ if (publish->header.bits.qos == 2)
+ mm->payload = publish->payload;
+ else
+ {
+ mm->payload = malloc(publish->payloadlen);
+ memcpy(mm->payload, publish->payload, publish->payloadlen);
+ }
+
+ mm->payloadlen = publish->payloadlen;
+ mm->qos = publish->header.bits.qos;
+ mm->retained = publish->header.bits.retain;
+ if (publish->header.bits.qos == 2)
+ mm->dup = 0; /* ensure that a QoS2 message is not passed to the application with dup = 1 */
+ else
+ mm->dup = publish->header.bits.dup;
+ mm->msgid = publish->msgId;
+
+ if (client->messageQueue->count == 0 && client->connected)
+ {
+ ListElement* found = NULL;
+
+ if ((found = ListFindItem(handles, client, clientStructCompare)) == NULL)
+ Log(LOG_ERROR, -1, "processPublication: did not find client structure in handles list");
+ else
+ {
+ MQTTAsyncs* m = (MQTTAsyncs*)(found->content);
+
+ if (m->ma)
+ rc = MQTTAsync_deliverMessage(m, publish->topic, publish->topiclen, mm);
+ }
+ }
+
+ if (rc == 0) /* if message was not delivered, queue it up */
+ {
+ qEntry* qe = malloc(sizeof(qEntry));
+ qe->msg = mm;
+ qe->topicName = publish->topic;
+ qe->topicLen = publish->topiclen;
+ ListAppend(client->messageQueue, qe, sizeof(qe) + sizeof(mm) + mm->payloadlen + strlen(qe->topicName)+1);
+#if !defined(NO_PERSISTENCE)
+ if (client->persistence)
+ MQTTPersistence_persistQueueEntry(client, (MQTTPersistence_qEntry*)qe);
+#endif
+ }
+ publish->topic = NULL;
+ FUNC_EXIT;
+}
+
+
+static int retryLoopInterval = 5;
+
+static void setRetryLoopInterval(int keepalive)
+{
+ int proposed = keepalive / 10;
+
+ if (proposed < 1)
+ proposed = 1;
+ else if (proposed > 5)
+ proposed = 5;
+ if (proposed < retryLoopInterval)
+ retryLoopInterval = proposed;
+}
+
+
+int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options)
+{
+ MQTTAsyncs* m = handle;
+ int rc = MQTTASYNC_SUCCESS;
+ MQTTAsync_queuedCommand* conn;
+
+ FUNC_ENTRY;
+ if (options == NULL)
+ {
+ rc = MQTTASYNC_NULL_PARAMETER;
+ goto exit;
+ }
+
+ if (strncmp(options->struct_id, "MQTC", 4) != 0 || options->struct_version < 0 || options->struct_version > 5)
+ {
+ rc = MQTTASYNC_BAD_STRUCTURE;
+ goto exit;
+ }
+
+#if defined(OPENSSL)
+ if (m->ssl && options->ssl == NULL)
+ {
+ rc = MQTTCLIENT_NULL_PARAMETER;
+ goto exit;
+ }
+#endif
+
+ if (options->will) /* check validity of will options structure */
+ {
+ if (strncmp(options->will->struct_id, "MQTW", 4) != 0 || (options->will->struct_version != 0 && options->will->struct_version != 1))
+ {
+ rc = MQTTASYNC_BAD_STRUCTURE;
+ goto exit;
+ }
+ if (options->will->qos < 0 || options->will->qos > 2)
+ {
+ rc = MQTTASYNC_BAD_QOS;
+ goto exit;
+ }
+ }
+ if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */
+ {
+ if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 2)
+ {
+ rc = MQTTASYNC_BAD_STRUCTURE;
+ goto exit;
+ }
+ }
+ if ((options->username && !UTF8_validateString(options->username)) ||
+ (options->password && !UTF8_validateString(options->password)))
+ {
+ rc = MQTTASYNC_BAD_UTF8_STRING;
+ goto exit;
+ }
+
+ m->connect.onSuccess = options->onSuccess;
+ m->connect.onFailure = options->onFailure;
+ m->connect.context = options->context;
+ m->connectTimeout = options->connectTimeout;
+
+ tostop = 0;
+ if (sendThread_state != STARTING && sendThread_state != RUNNING)
+ {
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ sendThread_state = STARTING;
+ Thread_start(MQTTAsync_sendThread, NULL);
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ }
+ if (receiveThread_state != STARTING && receiveThread_state != RUNNING)
+ {
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ receiveThread_state = STARTING;
+ Thread_start(MQTTAsync_receiveThread, handle);
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ }
+
+ m->c->keepAliveInterval = options->keepAliveInterval;
+ setRetryLoopInterval(options->keepAliveInterval);
+ m->c->cleansession = options->cleansession;
+ m->c->maxInflightMessages = options->maxInflight;
+ if (options->struct_version >= 3)
+ m->c->MQTTVersion = options->MQTTVersion;
+ else
+ m->c->MQTTVersion = 0;
+ if (options->struct_version >= 4)
+ {
+ m->automaticReconnect = options->automaticReconnect;
+ m->minRetryInterval = options->minRetryInterval;
+ m->maxRetryInterval = options->maxRetryInterval;
+ }
+
+ if (m->c->will)
+ {
+ free(m->c->will->payload);
+ free(m->c->will->topic);
+ free(m->c->will);
+ m->c->will = NULL;
+ }
+
+ if (options->will && (options->will->struct_version == 0 || options->will->struct_version == 1))
+ {
+ const void* source = NULL;
+
+ m->c->will = malloc(sizeof(willMessages));
+ if (options->will->message || (options->will->struct_version == 1 && options->will->payload.data))
+ {
+ if (options->will->struct_version == 1 && options->will->payload.data)
+ {
+ m->c->will->payloadlen = options->will->payload.len;
+ source = options->will->payload.data;
+ }
+ else
+ {
+ m->c->will->payloadlen = (int)strlen(options->will->message);
+ source = (void*)options->will->message;
+ }
+ m->c->will->payload = malloc(m->c->will->payloadlen);
+ memcpy(m->c->will->payload, source, m->c->will->payloadlen);
+ }
+ else
+ {
+ m->c->will->payload = NULL;
+ m->c->will->payloadlen = 0;
+ }
+ m->c->will->qos = options->will->qos;
+ m->c->will->retained = options->will->retained;
+ m->c->will->topic = MQTTStrdup(options->will->topicName);
+ }
+
+#if defined(OPENSSL)
+ if (m->c->sslopts)
+ {
+ if (m->c->sslopts->trustStore)
+ free((void*)m->c->sslopts->trustStore);
+ if (m->c->sslopts->keyStore)
+ free((void*)m->c->sslopts->keyStore);
+ if (m->c->sslopts->privateKey)
+ free((void*)m->c->sslopts->privateKey);
+ if (m->c->sslopts->privateKeyPassword)
+ free((void*)m->c->sslopts->privateKeyPassword);
+ if (m->c->sslopts->enabledCipherSuites)
+ free((void*)m->c->sslopts->enabledCipherSuites);
+ if (m->c->sslopts->struct_version >= 2)
+ {
+ if (m->c->sslopts->CApath)
+ free((void*)m->c->sslopts->CApath);
+ }
+ free(m->c->sslopts);
+ free((void*)m->c->sslopts);
+ m->c->sslopts = NULL;
+ }
+
+ if (options->struct_version != 0 && options->ssl)
+ {
+ m->c->sslopts = malloc(sizeof(MQTTClient_SSLOptions));
+ memset(m->c->sslopts, '\0', sizeof(MQTTClient_SSLOptions));
+ m->c->sslopts->struct_version = options->ssl->struct_version;
+ if (options->ssl->trustStore)
+ m->c->sslopts->trustStore = MQTTStrdup(options->ssl->trustStore);
+ if (options->ssl->keyStore)
+ m->c->sslopts->keyStore = MQTTStrdup(options->ssl->keyStore);
+ if (options->ssl->privateKey)
+ m->c->sslopts->privateKey = MQTTStrdup(options->ssl->privateKey);
+ if (options->ssl->privateKeyPassword)
+ m->c->sslopts->privateKeyPassword = MQTTStrdup(options->ssl->privateKeyPassword);
+ if (options->ssl->enabledCipherSuites)
+ m->c->sslopts->enabledCipherSuites = MQTTStrdup(options->ssl->enabledCipherSuites);
+ m->c->sslopts->enableServerCertAuth = options->ssl->enableServerCertAuth;
+ if (m->c->sslopts->struct_version >= 1)
+ m->c->sslopts->sslVersion = options->ssl->sslVersion;
+ if (m->c->sslopts->struct_version >= 2)
+ {
+ m->c->sslopts->verify = options->ssl->verify;
+ if (m->c->sslopts->CApath)
+ m->c->sslopts->CApath = MQTTStrdup(options->ssl->CApath);
+ }
+ }
+#else
+ if (options->struct_version != 0 && options->ssl)
+ {
+ rc = MQTTASYNC_SSL_NOT_SUPPORTED;
+ goto exit;
+ }
+#endif
+
+ if (m->c->username)
+ free((void*)m->c->username);
+ if (options->username)
+ m->c->username = MQTTStrdup(options->username);
+ if (m->c->password)
+ free((void*)m->c->password);
+ if (options->password)
+ {
+ m->c->password = MQTTStrdup(options->password);
+ m->c->passwordlen = (int)strlen(options->password);
+ }
+ else if (options->struct_version >= 5 && options->binarypwd.data)
+ {
+ m->c->passwordlen = options->binarypwd.len;
+ m->c->password = malloc(m->c->passwordlen);
+ memcpy((void*)m->c->password, options->binarypwd.data, m->c->passwordlen);
+ }
+
+ m->c->retryInterval = options->retryInterval;
+ m->shouldBeConnected = 1;
+
+ m->connectTimeout = options->connectTimeout;
+
+ MQTTAsync_freeServerURIs(m);
+ if (options->struct_version >= 2 && options->serverURIcount > 0)
+ {
+ int i;
+
+ m->serverURIcount = options->serverURIcount;
+ m->serverURIs = malloc(options->serverURIcount * sizeof(char*));
+ for (i = 0; i < options->serverURIcount; ++i)
+ m->serverURIs[i] = MQTTStrdup(options->serverURIs[i]);
+ }
+
+ /* Add connect request to operation queue */
+ conn = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(conn, '\0', sizeof(MQTTAsync_queuedCommand));
+ conn->client = m;
+ if (options)
+ {
+ conn->command.onSuccess = options->onSuccess;
+ conn->command.onFailure = options->onFailure;
+ conn->command.context = options->context;
+ }
+ conn->command.type = CONNECT;
+ conn->command.details.conn.currentURI = 0;
+ rc = MQTTAsync_addCommand(conn, sizeof(conn));
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static int MQTTAsync_disconnect1(MQTTAsync handle, const MQTTAsync_disconnectOptions* options, int internal)
+{
+ MQTTAsyncs* m = handle;
+ int rc = MQTTASYNC_SUCCESS;
+ MQTTAsync_queuedCommand* dis;
+
+ FUNC_ENTRY;
+ if (m == NULL || m->c == NULL)
+ {
+ rc = MQTTASYNC_FAILURE;
+ goto exit;
+ }
+ if (!internal)
+ m->shouldBeConnected = 0;
+ if (m->c->connected == 0)
+ {
+ rc = MQTTASYNC_DISCONNECTED;
+ goto exit;
+ }
+
+ /* Add disconnect request to operation queue */
+ dis = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(dis, '\0', sizeof(MQTTAsync_queuedCommand));
+ dis->client = m;
+ if (options)
+ {
+ dis->command.onSuccess = options->onSuccess;
+ dis->command.onFailure = options->onFailure;
+ dis->command.context = options->context;
+ dis->command.details.dis.timeout = options->timeout;
+ }
+ dis->command.type = DISCONNECT;
+ dis->command.details.dis.internal = internal;
+ rc = MQTTAsync_addCommand(dis, sizeof(dis));
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static int MQTTAsync_disconnect_internal(MQTTAsync handle, int timeout)
+{
+ MQTTAsync_disconnectOptions options = MQTTAsync_disconnectOptions_initializer;
+
+ options.timeout = timeout;
+ return MQTTAsync_disconnect1(handle, &options, 1);
+}
+
+
+void MQTTProtocol_closeSession(Clients* c, int sendwill)
+{
+ MQTTAsync_disconnect_internal((MQTTAsync)c->context, 0);
+}
+
+
+int MQTTAsync_disconnect(MQTTAsync handle, const MQTTAsync_disconnectOptions* options)
+{
+ return MQTTAsync_disconnect1(handle, options, 0);
+}
+
+
+int MQTTAsync_isConnected(MQTTAsync handle)
+{
+ MQTTAsyncs* m = handle;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ if (m && m->c)
+ rc = m->c->connected;
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static int cmdMessageIDCompare(void* a, void* b)
+{
+ MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)a;
+ return cmd->command.token == *(int*)b;
+}
+
+
+/**
+ * Assign a new message id for a client. Make sure it isn't already being used and does
+ * not exceed the maximum.
+ * @param m a client structure
+ * @return the next message id to use, or 0 if none available
+ */
+static int MQTTAsync_assignMsgId(MQTTAsyncs* m)
+{
+ int start_msgid = m->c->msgID;
+ int msgid = start_msgid;
+ thread_id_type thread_id = 0;
+ int locked = 0;
+
+ /* need to check: commands list and response list for a client */
+ FUNC_ENTRY;
+ /* We might be called in a callback. In which case, this mutex will be already locked. */
+ thread_id = Thread_getid();
+ if (thread_id != sendThread_id && thread_id != receiveThread_id)
+ {
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ locked = 1;
+ }
+
+ msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
+ while (ListFindItem(commands, &msgid, cmdMessageIDCompare) ||
+ ListFindItem(m->responses, &msgid, cmdMessageIDCompare))
+ {
+ msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
+ if (msgid == start_msgid)
+ { /* we've tried them all - none free */
+ msgid = 0;
+ break;
+ }
+ }
+ if (msgid != 0)
+ m->c->msgID = msgid;
+ if (locked)
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(msgid);
+ return msgid;
+}
+
+
+int MQTTAsync_subscribeMany(MQTTAsync handle, int count, char* const* topic, int* qos, MQTTAsync_responseOptions* response)
+{
+ MQTTAsyncs* m = handle;
+ int i = 0;
+ int rc = MQTTASYNC_FAILURE;
+ MQTTAsync_queuedCommand* sub;
+ int msgid = 0;
+
+ FUNC_ENTRY;
+ if (m == NULL || m->c == NULL)
+ {
+ rc = MQTTASYNC_FAILURE;
+ goto exit;
+ }
+ if (m->c->connected == 0)
+ {
+ rc = MQTTASYNC_DISCONNECTED;
+ goto exit;
+ }
+ for (i = 0; i < count; i++)
+ {
+ if (!UTF8_validateString(topic[i]))
+ {
+ rc = MQTTASYNC_BAD_UTF8_STRING;
+ goto exit;
+ }
+ if (qos[i] < 0 || qos[i] > 2)
+ {
+ rc = MQTTASYNC_BAD_QOS;
+ goto exit;
+ }
+ }
+ if ((msgid = MQTTAsync_assignMsgId(m)) == 0)
+ {
+ rc = MQTTASYNC_NO_MORE_MSGIDS;
+ goto exit;
+ }
+
+ /* Add subscribe request to operation queue */
+ sub = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(sub, '\0', sizeof(MQTTAsync_queuedCommand));
+ sub->client = m;
+ sub->command.token = msgid;
+ if (response)
+ {
+ sub->command.onSuccess = response->onSuccess;
+ sub->command.onFailure = response->onFailure;
+ sub->command.context = response->context;
+ response->token = sub->command.token;
+ }
+ sub->command.type = SUBSCRIBE;
+ sub->command.details.sub.count = count;
+ sub->command.details.sub.topics = malloc(sizeof(char*) * count);
+ sub->command.details.sub.qoss = malloc(sizeof(int) * count);
+ for (i = 0; i < count; ++i)
+ {
+ sub->command.details.sub.topics[i] = MQTTStrdup(topic[i]);
+ sub->command.details.sub.qoss[i] = qos[i];
+ }
+ rc = MQTTAsync_addCommand(sub, sizeof(sub));
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_subscribe(MQTTAsync handle, const char* topic, int qos, MQTTAsync_responseOptions* response)
+{
+ int rc = 0;
+ char *const topics[] = {(char*)topic};
+ FUNC_ENTRY;
+ rc = MQTTAsync_subscribeMany(handle, 1, topics, &qos, response);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_unsubscribeMany(MQTTAsync handle, int count, char* const* topic, MQTTAsync_responseOptions* response)
+{
+ MQTTAsyncs* m = handle;
+ int i = 0;
+ int rc = SOCKET_ERROR;
+ MQTTAsync_queuedCommand* unsub;
+ int msgid = 0;
+
+ FUNC_ENTRY;
+ if (m == NULL || m->c == NULL)
+ {
+ rc = MQTTASYNC_FAILURE;
+ goto exit;
+ }
+ if (m->c->connected == 0)
+ {
+ rc = MQTTASYNC_DISCONNECTED;
+ goto exit;
+ }
+ for (i = 0; i < count; i++)
+ {
+ if (!UTF8_validateString(topic[i]))
+ {
+ rc = MQTTASYNC_BAD_UTF8_STRING;
+ goto exit;
+ }
+ }
+ if ((msgid = MQTTAsync_assignMsgId(m)) == 0)
+ {
+ rc = MQTTASYNC_NO_MORE_MSGIDS;
+ goto exit;
+ }
+
+ /* Add unsubscribe request to operation queue */
+ unsub = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(unsub, '\0', sizeof(MQTTAsync_queuedCommand));
+ unsub->client = m;
+ unsub->command.type = UNSUBSCRIBE;
+ unsub->command.token = msgid;
+ if (response)
+ {
+ unsub->command.onSuccess = response->onSuccess;
+ unsub->command.onFailure = response->onFailure;
+ unsub->command.context = response->context;
+ response->token = unsub->command.token;
+ }
+ unsub->command.details.unsub.count = count;
+ unsub->command.details.unsub.topics = malloc(sizeof(char*) * count);
+ for (i = 0; i < count; ++i)
+ unsub->command.details.unsub.topics[i] = MQTTStrdup(topic[i]);
+ rc = MQTTAsync_addCommand(unsub, sizeof(unsub));
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_unsubscribe(MQTTAsync handle, const char* topic, MQTTAsync_responseOptions* response)
+{
+ int rc = 0;
+ char *const topics[] = {(char*)topic};
+ FUNC_ENTRY;
+ rc = MQTTAsync_unsubscribeMany(handle, 1, topics, response);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static int MQTTAsync_countBufferedMessages(MQTTAsyncs* m)
+{
+ ListElement* current = NULL;
+ int count = 0;
+
+ while (ListNextElement(commands, &current))
+ {
+ MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
+
+ if (cmd->client == m && cmd->command.type == PUBLISH)
+ count++;
+ }
+ return count;
+}
+
+
+int MQTTAsync_send(MQTTAsync handle, const char* destinationName, int payloadlen, void* payload,
+ int qos, int retained, MQTTAsync_responseOptions* response)
+{
+ int rc = MQTTASYNC_SUCCESS;
+ MQTTAsyncs* m = handle;
+ MQTTAsync_queuedCommand* pub;
+ int msgid = 0;
+
+ FUNC_ENTRY;
+ if (m == NULL || m->c == NULL)
+ rc = MQTTASYNC_FAILURE;
+ else if (m->c->connected == 0 && (m->createOptions == NULL ||
+ m->createOptions->sendWhileDisconnected == 0 || m->shouldBeConnected == 0))
+ rc = MQTTASYNC_DISCONNECTED;
+ else if (!UTF8_validateString(destinationName))
+ rc = MQTTASYNC_BAD_UTF8_STRING;
+ else if (qos < 0 || qos > 2)
+ rc = MQTTASYNC_BAD_QOS;
+ else if (qos > 0 && (msgid = MQTTAsync_assignMsgId(m)) == 0)
+ rc = MQTTASYNC_NO_MORE_MSGIDS;
+ else if (m->createOptions && (MQTTAsync_countBufferedMessages(m) >= m->createOptions->maxBufferedMessages))
+ rc = MQTTASYNC_MAX_BUFFERED_MESSAGES;
+
+ if (rc != MQTTASYNC_SUCCESS)
+ goto exit;
+
+ /* Add publish request to operation queue */
+ pub = malloc(sizeof(MQTTAsync_queuedCommand));
+ memset(pub, '\0', sizeof(MQTTAsync_queuedCommand));
+ pub->client = m;
+ pub->command.type = PUBLISH;
+ pub->command.token = msgid;
+ if (response)
+ {
+ pub->command.onSuccess = response->onSuccess;
+ pub->command.onFailure = response->onFailure;
+ pub->command.context = response->context;
+ response->token = pub->command.token;
+ }
+ pub->command.details.pub.destinationName = MQTTStrdup(destinationName);
+ pub->command.details.pub.payloadlen = payloadlen;
+ pub->command.details.pub.payload = malloc(payloadlen);
+ memcpy(pub->command.details.pub.payload, payload, payloadlen);
+ pub->command.details.pub.qos = qos;
+ pub->command.details.pub.retained = retained;
+ rc = MQTTAsync_addCommand(pub, sizeof(pub));
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+int MQTTAsync_sendMessage(MQTTAsync handle, const char* destinationName, const MQTTAsync_message* message,
+ MQTTAsync_responseOptions* response)
+{
+ int rc = MQTTASYNC_SUCCESS;
+
+ FUNC_ENTRY;
+ if (message == NULL)
+ {
+ rc = MQTTASYNC_NULL_PARAMETER;
+ goto exit;
+ }
+ if (strncmp(message->struct_id, "MQTM", 4) != 0 || message->struct_version != 0)
+ {
+ rc = MQTTASYNC_BAD_STRUCTURE;
+ goto exit;
+ }
+
+ rc = MQTTAsync_send(handle, destinationName, message->payloadlen, message->payload,
+ message->qos, message->retained, response);
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void MQTTAsync_retry(void)
+{
+ static time_t last = 0L;
+ time_t now;
+
+ FUNC_ENTRY;
+ time(&(now));
+ if (difftime(now, last) > retryLoopInterval)
+ {
+ time(&(last));
+ MQTTProtocol_keepalive(now);
+ MQTTProtocol_retry(now, 1, 0);
+ }
+ else
+ MQTTProtocol_retry(now, 0, 0);
+ FUNC_EXIT;
+}
+
+
+static int MQTTAsync_connecting(MQTTAsyncs* m)
+{
+ int rc = -1;
+
+ FUNC_ENTRY;
+ if (m->c->connect_state == 1) /* TCP connect started - check for completion */
+ {
+ int error;
+ socklen_t len = sizeof(error);
+
+ if ((rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
+ rc = error;
+
+ if (rc != 0)
+ goto exit;
+
+ Socket_clearPendingWrite(m->c->net.socket);
+
+#if defined(OPENSSL)
+ if (m->ssl)
+ {
+ int port;
+ char* hostname;
+ int setSocketForSSLrc = 0;
+
+ hostname = MQTTProtocol_addressPort(m->serverURI, &port);
+ setSocketForSSLrc = SSLSocket_setSocketForSSL(&m->c->net, m->c->sslopts, hostname);
+ if (hostname != m->serverURI)
+ free(hostname);
+
+ if (setSocketForSSLrc != MQTTASYNC_SUCCESS)
+ {
+ if (m->c->session != NULL)
+ if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1)
+ Log(TRACE_MIN, -1, "Failed to set SSL session with stored data, non critical");
+ rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket,
+ m->serverURI, m->c->sslopts->verify);
+ if (rc == TCPSOCKET_INTERRUPTED)
+ {
+ rc = MQTTCLIENT_SUCCESS; /* the connect is still in progress */
+ m->c->connect_state = 2;
+ }
+ else if (rc == SSL_FATAL)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ else if (rc == 1)
+ {
+ rc = MQTTCLIENT_SUCCESS;
+ m->c->connect_state = 3;
+ if (MQTTPacket_send_connect(m->c, m->connect.details.conn.MQTTVersion) == SOCKET_ERROR)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ if (!m->c->cleansession && m->c->session == NULL)
+ m->c->session = SSL_get1_session(m->c->net.ssl);
+ }
+ }
+ else
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ }
+ else
+ {
+#endif
+ m->c->connect_state = 3; /* TCP/SSL connect completed, in which case send the MQTT connect packet */
+ if ((rc = MQTTPacket_send_connect(m->c, m->connect.details.conn.MQTTVersion)) == SOCKET_ERROR)
+ goto exit;
+#if defined(OPENSSL)
+ }
+#endif
+ }
+#if defined(OPENSSL)
+ else if (m->c->connect_state == 2) /* SSL connect sent - wait for completion */
+ {
+ if ((rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket,
+ m->serverURI, m->c->sslopts->verify)) != 1)
+ goto exit;
+
+ if(!m->c->cleansession && m->c->session == NULL)
+ m->c->session = SSL_get1_session(m->c->net.ssl);
+ m->c->connect_state = 3; /* SSL connect completed, in which case send the MQTT connect packet */
+ if ((rc = MQTTPacket_send_connect(m->c, m->connect.details.conn.MQTTVersion)) == SOCKET_ERROR)
+ goto exit;
+ }
+#endif
+
+exit:
+ if ((rc != 0 && rc != TCPSOCKET_INTERRUPTED && m->c->connect_state != 2) || (rc == SSL_FATAL))
+ nextOrClose(m, MQTTASYNC_FAILURE, "TCP/TLS connect failure");
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static MQTTPacket* MQTTAsync_cycle(int* sock, unsigned long timeout, int* rc)
+{
+ struct timeval tp = {0L, 0L};
+ static Ack ack;
+ MQTTPacket* pack = NULL;
+
+ FUNC_ENTRY;
+ if (timeout > 0L)
+ {
+ tp.tv_sec = timeout / 1000;
+ tp.tv_usec = (timeout % 1000) * 1000; /* this field is microseconds! */
+ }
+
+#if defined(OPENSSL)
+ if ((*sock = SSLSocket_getPendingRead()) == -1)
+ {
+#endif
+ /* 0 from getReadySocket indicates no work to do, -1 == error, but can happen normally */
+ *sock = Socket_getReadySocket(0, &tp,socket_mutex);
+ if (!tostop && *sock == 0 && (tp.tv_sec > 0L || tp.tv_usec > 0L))
+ MQTTAsync_sleep(100L);
+#if defined(OPENSSL)
+ }
+#endif
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ if (*sock > 0)
+ {
+ MQTTAsyncs* m = NULL;
+ if (ListFindItem(handles, sock, clientSockCompare) != NULL)
+ m = (MQTTAsync)(handles->current->content);
+ if (m != NULL)
+ {
+ Log(TRACE_MINIMUM, -1, "m->c->connect_state = %d",m->c->connect_state);
+ if (m->c->connect_state == 1 || m->c->connect_state == 2)
+ *rc = MQTTAsync_connecting(m);
+ else
+ pack = MQTTPacket_Factory(&m->c->net, rc);
+ if (m->c->connect_state == 3 && *rc == SOCKET_ERROR)
+ {
+ Log(TRACE_MINIMUM, -1, "CONNECT sent but MQTTPacket_Factory has returned SOCKET_ERROR");
+ nextOrClose(m, MQTTASYNC_FAILURE, "TCP connect completion failure");
+ }
+ else
+ {
+ Log(TRACE_MINIMUM, -1, "m->c->connect_state = %d",m->c->connect_state);
+ Log(TRACE_MINIMUM, -1, "CONNECT sent, *rc is %d",*rc);
+ }
+ }
+ if (pack)
+ {
+ int freed = 1;
+
+ /* Note that these handle... functions free the packet structure that they are dealing with */
+ if (pack->header.bits.type == PUBLISH)
+ *rc = MQTTProtocol_handlePublishes(pack, *sock);
+ else if (pack->header.bits.type == PUBACK || pack->header.bits.type == PUBCOMP)
+ {
+ int msgid;
+
+ ack = (pack->header.bits.type == PUBCOMP) ? *(Pubcomp*)pack : *(Puback*)pack;
+ msgid = ack.msgId;
+ *rc = (pack->header.bits.type == PUBCOMP) ?
+ MQTTProtocol_handlePubcomps(pack, *sock) : MQTTProtocol_handlePubacks(pack, *sock);
+ if (!m)
+ Log(LOG_ERROR, -1, "PUBCOMP or PUBACK received for no client, msgid %d", msgid);
+ if (m)
+ {
+ ListElement* current = NULL;
+
+ if (m->dc)
+ {
+ Log(TRACE_MIN, -1, "Calling deliveryComplete for client %s, msgid %d", m->c->clientID, msgid);
+ (*(m->dc))(m->context, msgid);
+ }
+ /* use the msgid to find the callback to be called */
+ while (ListNextElement(m->responses, &current))
+ {
+ MQTTAsync_queuedCommand* command = (MQTTAsync_queuedCommand*)(current->content);
+ if (command->command.token == msgid)
+ {
+ if (!ListDetach(m->responses, command)) /* then remove the response from the list */
+ Log(LOG_ERROR, -1, "Publish command not removed from command list");
+ if (command->command.onSuccess)
+ {
+ MQTTAsync_successData data;
+
+ data.token = command->command.token;
+ data.alt.pub.destinationName = command->command.details.pub.destinationName;
+ data.alt.pub.message.payload = command->command.details.pub.payload;
+ data.alt.pub.message.payloadlen = command->command.details.pub.payloadlen;
+ data.alt.pub.message.qos = command->command.details.pub.qos;
+ data.alt.pub.message.retained = command->command.details.pub.retained;
+ Log(TRACE_MIN, -1, "Calling publish success for client %s", m->c->clientID);
+ (*(command->command.onSuccess))(command->command.context, &data);
+ }
+ MQTTAsync_freeCommand(command);
+ break;
+ }
+ }
+ }
+ }
+ else if (pack->header.bits.type == PUBREC)
+ *rc = MQTTProtocol_handlePubrecs(pack, *sock);
+ else if (pack->header.bits.type == PUBREL)
+ *rc = MQTTProtocol_handlePubrels(pack, *sock);
+ else if (pack->header.bits.type == PINGRESP)
+ *rc = MQTTProtocol_handlePingresps(pack, *sock);
+ else
+ freed = 0;
+ if (freed)
+ pack = NULL;
+ }
+ }
+ MQTTAsync_retry();
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(*rc);
+ return pack;
+}
+
+/*
+static int pubCompare(void* a, void* b)
+{
+ Messages* msg = (Messages*)a;
+ return msg->publish == (Publications*)b;
+}*/
+
+
+int MQTTAsync_getPendingTokens(MQTTAsync handle, MQTTAsync_token **tokens)
+{
+ int rc = MQTTASYNC_SUCCESS;
+ MQTTAsyncs* m = handle;
+ ListElement* current = NULL;
+ int count = 0;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+ *tokens = NULL;
+
+ if (m == NULL)
+ {
+ rc = MQTTASYNC_FAILURE;
+ goto exit;
+ }
+
+ /* calculate the number of pending tokens - commands plus inflight */
+ while (ListNextElement(commands, &current))
+ {
+ MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
+
+ if (cmd->client == m)
+ count++;
+ }
+ if (m->c)
+ count += m->c->outboundMsgs->count;
+ if (count == 0)
+ goto exit; /* no tokens to return */
+ *tokens = malloc(sizeof(MQTTAsync_token) * (count + 1)); /* add space for sentinel at end of list */
+
+ /* First add the unprocessed commands to the pending tokens */
+ current = NULL;
+ count = 0;
+ while (ListNextElement(commands, &current))
+ {
+ MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
+
+ if (cmd->client == m)
+ (*tokens)[count++] = cmd->command.token;
+ }
+
+ /* Now add the inflight messages */
+ if (m->c && m->c->outboundMsgs->count > 0)
+ {
+ current = NULL;
+ while (ListNextElement(m->c->outboundMsgs, &current))
+ {
+ Messages* m = (Messages*)(current->content);
+ (*tokens)[count++] = m->msgid;
+ }
+ }
+ (*tokens)[count] = -1; /* indicate end of list */
+
+exit:
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_isComplete(MQTTAsync handle, MQTTAsync_token dt)
+{
+ int rc = MQTTASYNC_SUCCESS;
+ MQTTAsyncs* m = handle;
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+
+ if (m == NULL)
+ {
+ rc = MQTTASYNC_FAILURE;
+ goto exit;
+ }
+
+ /* First check unprocessed commands */
+ current = NULL;
+ while (ListNextElement(commands, &current))
+ {
+ MQTTAsync_queuedCommand* cmd = (MQTTAsync_queuedCommand*)(current->content);
+
+ if (cmd->client == m && cmd->command.token == dt)
+ goto exit;
+ }
+
+ /* Now check the inflight messages */
+ if (m->c && m->c->outboundMsgs->count > 0)
+ {
+ current = NULL;
+ while (ListNextElement(m->c->outboundMsgs, &current))
+ {
+ Messages* m = (Messages*)(current->content);
+ if (m->msgid == dt)
+ goto exit;
+ }
+ }
+ rc = MQTTASYNC_TRUE; /* Can't find it, so it must be complete */
+
+exit:
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTAsync_waitForCompletion(MQTTAsync handle, MQTTAsync_token dt, unsigned long timeout)
+{
+ int rc = MQTTASYNC_FAILURE;
+ START_TIME_TYPE start = MQTTAsync_start_clock();
+ unsigned long elapsed = 0L;
+ MQTTAsyncs* m = handle;
+
+ FUNC_ENTRY;
+ MQTTAsync_lock_mutex(mqttasync_mutex);
+
+ if (m == NULL || m->c == NULL)
+ {
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ rc = MQTTASYNC_FAILURE;
+ goto exit;
+ }
+ if (m->c->connected == 0)
+ {
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+ rc = MQTTASYNC_DISCONNECTED;
+ goto exit;
+ }
+ MQTTAsync_unlock_mutex(mqttasync_mutex);
+
+ if (MQTTAsync_isComplete(handle, dt) == 1)
+ {
+ rc = MQTTASYNC_SUCCESS; /* well we couldn't find it */
+ goto exit;
+ }
+
+ elapsed = MQTTAsync_elapsed(start);
+ while (elapsed < timeout)
+ {
+ MQTTAsync_sleep(100);
+ if (MQTTAsync_isComplete(handle, dt) == 1)
+ {
+ rc = MQTTASYNC_SUCCESS; /* well we couldn't find it */
+ goto exit;
+ }
+ elapsed = MQTTAsync_elapsed(start);
+ }
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+void MQTTAsync_setTraceLevel(enum MQTTASYNC_TRACE_LEVELS level)
+{
+ Log_setTraceLevel((enum LOG_LEVELS)level);
+}
+
+
+void MQTTAsync_setTraceCallback(MQTTAsync_traceCallback* callback)
+{
+ Log_setTraceCallback((Log_traceCallback*)callback);
+}
+
+
+MQTTAsync_nameValue* MQTTAsync_getVersionInfo(void)
+{
+ #define MAX_INFO_STRINGS 8
+ static MQTTAsync_nameValue libinfo[MAX_INFO_STRINGS + 1];
+ int i = 0;
+
+ libinfo[i].name = "Product name";
+ libinfo[i++].value = "Paho Asynchronous MQTT C Client Library";
+
+ libinfo[i].name = "Version";
+ libinfo[i++].value = CLIENT_VERSION;
+
+ libinfo[i].name = "Build level";
+ libinfo[i++].value = BUILD_TIMESTAMP;
+#if defined(OPENSSL)
+ libinfo[i].name = "OpenSSL version";
+ libinfo[i++].value = SSLeay_version(SSLEAY_VERSION);
+
+ libinfo[i].name = "OpenSSL flags";
+ libinfo[i++].value = SSLeay_version(SSLEAY_CFLAGS);
+
+ libinfo[i].name = "OpenSSL build timestamp";
+ libinfo[i++].value = SSLeay_version(SSLEAY_BUILT_ON);
+
+ libinfo[i].name = "OpenSSL platform";
+ libinfo[i++].value = SSLeay_version(SSLEAY_PLATFORM);
+
+ libinfo[i].name = "OpenSSL directory";
+ libinfo[i++].value = SSLeay_version(SSLEAY_DIR);
+#endif
+ libinfo[i].name = NULL;
+ libinfo[i].value = NULL;
+ return libinfo;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTAsync.h b/tts-mqtt-test/src/paho-mqtt/MQTTAsync.h
new file mode 100644
index 0000000..0261c0d
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTAsync.h
@@ -0,0 +1,1751 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation
+ * Ian Craggs, Allan Stockdill-Mander - SSL connections
+ * Ian Craggs - multiple server connection support
+ * Ian Craggs - MQTT 3.1.1 support
+ * Ian Craggs - fix for bug 444103 - success/failure callbacks not invoked
+ * Ian Craggs - automatic reconnect and offline buffering (send while disconnected)
+ * Ian Craggs - binary will message
+ * Ian Craggs - binary password
+ * Ian Craggs - remove const on eyecatchers #168
+ *******************************************************************************/
+
+/********************************************************************/
+
+/**
+ * @cond MQTTAsync_main
+ * @mainpage Asynchronous MQTT client library for C
+ *
+ * &copy; Copyright IBM Corp. 2009, 2017
+ *
+ * @brief An Asynchronous MQTT client library for C.
+ *
+ * An MQTT client application connects to MQTT-capable servers.
+ * A typical client is responsible for collecting information from a telemetry
+ * device and publishing the information to the server. It can also subscribe
+ * to topics, receive messages, and use this information to control the
+ * telemetry device.
+ *
+ * MQTT clients implement the published MQTT v3 protocol. You can write your own
+ * API to the MQTT protocol using the programming language and platform of your
+ * choice. This can be time-consuming and error-prone.
+ *
+ * To simplify writing MQTT client applications, this library encapsulates
+ * the MQTT v3 protocol for you. Using this library enables a fully functional
+ * MQTT client application to be written in a few lines of code.
+ * The information presented here documents the API provided
+ * by the Asynchronous MQTT Client library for C.
+ *
+ * <b>Using the client</b><br>
+ * Applications that use the client library typically use a similar structure:
+ * <ul>
+ * <li>Create a client object</li>
+ * <li>Set the options to connect to an MQTT server</li>
+ * <li>Set up callback functions</li>
+ * <li>Connect the client to an MQTT server</li>
+ * <li>Subscribe to any topics the client needs to receive</li>
+ * <li>Repeat until finished:</li>
+ * <ul>
+ * <li>Publish any messages the client needs to</li>
+ * <li>Handle any incoming messages</li>
+ * </ul>
+ * <li>Disconnect the client</li>
+ * <li>Free any memory being used by the client</li>
+ * </ul>
+ * Some simple examples are shown here:
+ * <ul>
+ * <li>@ref publish</li>
+ * <li>@ref subscribe</li>
+ * </ul>
+ * Additional information about important concepts is provided here:
+ * <ul>
+ * <li>@ref async</li>
+ * <li>@ref wildcard</li>
+ * <li>@ref qos</li>
+ * <li>@ref tracing</li>
+ * <li>@ref auto_reconnect</li>
+ * <li>@ref offline_publish</li>
+ * </ul>
+ * @endcond
+ */
+
+
+/*
+/// @cond EXCLUDE
+*/
+#if defined(__cplusplus)
+ extern "C" {
+#endif
+
+#if !defined(MQTTASYNC_H)
+#define MQTTASYNC_H
+
+#if defined(WIN32) || defined(WIN64)
+ #define DLLImport __declspec(dllimport)
+ #define DLLExport __declspec(dllexport)
+#else
+ #define DLLImport extern
+ #define DLLExport __attribute__ ((visibility ("default")))
+#endif
+
+#include <stdio.h>
+/*
+/// @endcond
+*/
+
+#if !defined(NO_PERSISTENCE)
+#include "../paho-mqtt/MQTTClientPersistence.h"
+#endif
+
+/**
+ * Return code: No error. Indicates successful completion of an MQTT client
+ * operation.
+ */
+#define MQTTASYNC_SUCCESS 0
+/**
+ * Return code: A generic error code indicating the failure of an MQTT client
+ * operation.
+ */
+#define MQTTASYNC_FAILURE -1
+
+/* error code -2 is MQTTAsync_PERSISTENCE_ERROR */
+
+#define MQTTASYNC_PERSISTENCE_ERROR -2
+
+/**
+ * Return code: The client is disconnected.
+ */
+#define MQTTASYNC_DISCONNECTED -3
+/**
+ * Return code: The maximum number of messages allowed to be simultaneously
+ * in-flight has been reached.
+ */
+#define MQTTASYNC_MAX_MESSAGES_INFLIGHT -4
+/**
+ * Return code: An invalid UTF-8 string has been detected.
+ */
+#define MQTTASYNC_BAD_UTF8_STRING -5
+/**
+ * Return code: A NULL parameter has been supplied when this is invalid.
+ */
+#define MQTTASYNC_NULL_PARAMETER -6
+/**
+ * Return code: The topic has been truncated (the topic string includes
+ * embedded NULL characters). String functions will not access the full topic.
+ * Use the topic length value to access the full topic.
+ */
+#define MQTTASYNC_TOPICNAME_TRUNCATED -7
+/**
+ * Return code: A structure parameter does not have the correct eyecatcher
+ * and version number.
+ */
+#define MQTTASYNC_BAD_STRUCTURE -8
+/**
+ * Return code: A qos parameter is not 0, 1 or 2
+ */
+#define MQTTASYNC_BAD_QOS -9
+/**
+ * Return code: All 65535 MQTT msgids are being used
+ */
+#define MQTTASYNC_NO_MORE_MSGIDS -10
+/**
+ * Return code: the request is being discarded when not complete
+ */
+#define MQTTASYNC_OPERATION_INCOMPLETE -11
+/**
+ * Return code: no more messages can be buffered
+ */
+#define MQTTASYNC_MAX_BUFFERED_MESSAGES -12
+/**
+ * Return code: Attempting SSL connection using non-SSL version of library
+ */
+#define MQTTASYNC_SSL_NOT_SUPPORTED -13
+/**
+ * Return code: protocol prefix in serverURI should be tcp:// or ssl://
+ */
+#define MQTTASYNC_BAD_PROTOCOL -14
+
+
+/**
+ * Default MQTT version to connect with. Use 3.1.1 then fall back to 3.1
+ */
+#define MQTTVERSION_DEFAULT 0
+/**
+ * MQTT version to connect with: 3.1
+ */
+#define MQTTVERSION_3_1 3
+/**
+ * MQTT version to connect with: 3.1.1
+ */
+#define MQTTVERSION_3_1_1 4
+/**
+ * Bad return code from subscribe, as defined in the 3.1.1 specification
+ */
+#define MQTT_BAD_SUBSCRIBE 0x80
+
+
+/**
+ * Initialization options
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. Must be MQTG. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 */
+ int struct_version;
+ /** 1 = we do openssl init, 0 = leave it to the application */
+ int do_openssl_init;
+} MQTTAsync_init_options;
+
+#define MQTTAsync_init_options_initializer { {'M', 'Q', 'T', 'G'}, 0, 0 }
+
+/**
+ * Global init of mqtt library. Call once on program start to set global behaviour.
+ * handle_openssl_init - if mqtt library should handle openssl init (1) or rely on the caller to init it before using mqtt (0)
+ */
+DLLExport void MQTTAsync_global_init(MQTTAsync_init_options* inits);
+
+/**
+ * A handle representing an MQTT client. A valid client handle is available
+ * following a successful call to MQTTAsync_create().
+ */
+typedef void* MQTTAsync;
+/**
+ * A value representing an MQTT message. A token is returned to the
+ * client application when a message is published. The token can then be used to
+ * check that the message was successfully delivered to its destination (see
+ * MQTTAsync_publish(),
+ * MQTTAsync_publishMessage(),
+ * MQTTAsync_deliveryComplete(), and
+ * MQTTAsync_getPendingTokens()).
+ */
+typedef int MQTTAsync_token;
+
+/**
+ * A structure representing the payload and attributes of an MQTT message. The
+ * message topic is not part of this structure (see MQTTAsync_publishMessage(),
+ * MQTTAsync_publish(), MQTTAsync_receive(), MQTTAsync_freeMessage()
+ * and MQTTAsync_messageArrived()).
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. must be MQTM. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 */
+ int struct_version;
+ /** The length of the MQTT message payload in bytes. */
+ int payloadlen;
+ /** A pointer to the payload of the MQTT message. */
+ void* payload;
+ /**
+ * The quality of service (QoS) assigned to the message.
+ * There are three levels of QoS:
+ * <DL>
+ * <DT><B>QoS0</B></DT>
+ * <DD>Fire and forget - the message may not be delivered</DD>
+ * <DT><B>QoS1</B></DT>
+ * <DD>At least once - the message will be delivered, but may be
+ * delivered more than once in some circumstances.</DD>
+ * <DT><B>QoS2</B></DT>
+ * <DD>Once and one only - the message will be delivered exactly once.</DD>
+ * </DL>
+ */
+ int qos;
+ /**
+ * The retained flag serves two purposes depending on whether the message
+ * it is associated with is being published or received.
+ *
+ * <b>retained = true</b><br>
+ * For messages being published, a true setting indicates that the MQTT
+ * server should retain a copy of the message. The message will then be
+ * transmitted to new subscribers to a topic that matches the message topic.
+ * For subscribers registering a new subscription, the flag being true
+ * indicates that the received message is not a new one, but one that has
+ * been retained by the MQTT server.
+ *
+ * <b>retained = false</b> <br>
+ * For publishers, this ndicates that this message should not be retained
+ * by the MQTT server. For subscribers, a false setting indicates this is
+ * a normal message, received as a result of it being published to the
+ * server.
+ */
+ int retained;
+ /**
+ * The dup flag indicates whether or not this message is a duplicate.
+ * It is only meaningful when receiving QoS1 messages. When true, the
+ * client application should take appropriate action to deal with the
+ * duplicate message.
+ */
+ int dup;
+ /** The message identifier is normally reserved for internal use by the
+ * MQTT client and server.
+ */
+ int msgid;
+} MQTTAsync_message;
+
+#define MQTTAsync_message_initializer { {'M', 'Q', 'T', 'M'}, 0, 0, NULL, 0, 0, 0, 0 }
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * receipt of messages. The function is registered with the client library by
+ * passing it as an argument to MQTTAsync_setCallbacks(). It is
+ * called by the client library when a new message that matches a client
+ * subscription has been received from the server. This function is executed on
+ * a separate thread to the one on which the client application is running.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * MQTTAsync_setCallbacks(), which contains any application-specific context.
+ * @param topicName The topic associated with the received message.
+ * @param topicLen The length of the topic if there are one
+ * more NULL characters embedded in <i>topicName</i>, otherwise <i>topicLen</i>
+ * is 0. If <i>topicLen</i> is 0, the value returned by <i>strlen(topicName)</i>
+ * can be trusted. If <i>topicLen</i> is greater than 0, the full topic name
+ * can be retrieved by accessing <i>topicName</i> as a byte array of length
+ * <i>topicLen</i>.
+ * @param message The MQTTAsync_message structure for the received message.
+ * This structure contains the message payload and attributes.
+ * @return This function must return a boolean value indicating whether or not
+ * the message has been safely received by the client application. Returning
+ * true indicates that the message has been successfully handled.
+ * Returning false indicates that there was a problem. In this
+ * case, the client library will reinvoke MQTTAsync_messageArrived() to
+ * attempt to deliver the message to the application again.
+ */
+typedef int MQTTAsync_messageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* message);
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * notification of delivery of messages to the server. The function is
+ * registered with the client library by passing it as an argument to MQTTAsync_setCallbacks().
+ * It is called by the client library after the client application has
+ * published a message to the server. It indicates that the necessary
+ * handshaking and acknowledgements for the requested quality of service (see
+ * MQTTAsync_message.qos) have been completed. This function is executed on a
+ * separate thread to the one on which the client application is running.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * MQTTAsync_setCallbacks(), which contains any application-specific context.
+ * @param token The ::MQTTAsync_token associated with
+ * the published message. Applications can check that all messages have been
+ * correctly published by matching the tokens returned from calls to
+ * MQTTAsync_send() and MQTTAsync_sendMessage() with the tokens passed
+ * to this callback.
+ */
+typedef void MQTTAsync_deliveryComplete(void* context, MQTTAsync_token token);
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * notification of the loss of connection to the server. The function is
+ * registered with the client library by passing it as an argument to
+ * MQTTAsync_setCallbacks(). It is called by the client library if the client
+ * loses its connection to the server. The client application must take
+ * appropriate action, such as trying to reconnect or reporting the problem.
+ * This function is executed on a separate thread to the one on which the
+ * client application is running.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * MQTTAsync_setCallbacks(), which contains any application-specific context.
+ * @param cause The reason for the disconnection.
+ * Currently, <i>cause</i> is always set to NULL.
+ */
+typedef void MQTTAsync_connectionLost(void* context, char* cause);
+
+
+/**
+ * This is a callback function, which will be called when the client
+ * library successfully connects. This is superfluous when the connection
+ * is made in response to a MQTTAsync_connect call, because the onSuccess
+ * callback can be used. It is intended for use when automatic reconnect
+ * is enabled, so that when a reconnection attempt succeeds in the background,
+ * the application is notified and can take any required actions.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * MQTTAsync_setCallbacks(), which contains any application-specific context.
+ * @param cause The reason for the disconnection.
+ * Currently, <i>cause</i> is always set to NULL.
+ */
+typedef void MQTTAsync_connected(void* context, char* cause);
+
+
+
+/** The data returned on completion of an unsuccessful API call in the response callback onFailure. */
+typedef struct
+{
+ /** A token identifying the failed request. */
+ MQTTAsync_token token;
+ /** A numeric code identifying the error. */
+ int code;
+ /** Optional text explaining the error. Can be NULL. */
+ const char *message;
+} MQTTAsync_failureData;
+
+/** The data returned on completion of a successful API call in the response callback onSuccess. */
+typedef struct
+{
+ /** A token identifying the successful request. Can be used to refer to the request later. */
+ MQTTAsync_token token;
+ /** A union of the different values that can be returned for subscribe, unsubscribe and publish. */
+ union
+ {
+ /** For subscribe, the granted QoS of the subscription returned by the server. */
+ int qos;
+ /** For subscribeMany, the list of granted QoSs of the subscriptions returned by the server. */
+ int* qosList;
+ /** For publish, the message being sent to the server. */
+ struct
+ {
+ MQTTAsync_message message;
+ char* destinationName;
+ } pub;
+ /* For connect, the server connected to, MQTT version used, and sessionPresent flag */
+ struct
+ {
+ char* serverURI;
+ int MQTTVersion;
+ int sessionPresent;
+ } connect;
+ } alt;
+} MQTTAsync_successData;
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * notification of the successful completion of an API call. The function is
+ * registered with the client library by passing it as an argument in
+ * ::MQTTAsync_responseOptions.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * ::MQTTAsync_responseOptions, which contains any application-specific context.
+ * @param response Any success data associated with the API completion.
+ */
+typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * notification of the unsuccessful completion of an API call. The function is
+ * registered with the client library by passing it as an argument in
+ * ::MQTTAsync_responseOptions.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * ::MQTTAsync_responseOptions, which contains any application-specific context.
+ * @param response Any failure data associated with the API completion.
+ */
+typedef void MQTTAsync_onFailure(void* context, MQTTAsync_failureData* response);
+
+typedef struct
+{
+ /** The eyecatcher for this structure. Must be MQTR */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 */
+ int struct_version;
+ /**
+ * A pointer to a callback function to be called if the API call successfully
+ * completes. Can be set to NULL, in which case no indication of successful
+ * completion will be received.
+ */
+ MQTTAsync_onSuccess* onSuccess;
+ /**
+ * A pointer to a callback function to be called if the API call fails.
+ * Can be set to NULL, in which case no indication of unsuccessful
+ * completion will be received.
+ */
+ MQTTAsync_onFailure* onFailure;
+ /**
+ * A pointer to any application-specific context. The
+ * the <i>context</i> pointer is passed to success or failure callback functions to
+ * provide access to the context information in the callback.
+ */
+ void* context;
+ /**
+ * A token is returned from the call. It can be used to track
+ * the state of this request, both in the callbacks and in future calls
+ * such as ::MQTTAsync_waitForCompletion.
+ */
+ MQTTAsync_token token;
+} MQTTAsync_responseOptions;
+
+#define MQTTAsync_responseOptions_initializer { {'M', 'Q', 'T', 'R'}, 0, NULL, NULL, 0, 0 }
+
+
+/**
+ * This function sets the global callback functions for a specific client.
+ * If your client application doesn't use a particular callback, set the
+ * relevant parameter to NULL. Any necessary message acknowledgements and
+ * status communications are handled in the background without any intervention
+ * from the client application. If you do not set a messageArrived callback
+ * function, you will not be notified of the receipt of any messages as a
+ * result of a subscription.
+ *
+ * <b>Note:</b> The MQTT client must be disconnected when this function is
+ * called.
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param context A pointer to any application-specific context. The
+ * the <i>context</i> pointer is passed to each of the callback functions to
+ * provide access to the context information in the callback.
+ * @param cl A pointer to an MQTTAsync_connectionLost() callback
+ * function. You can set this to NULL if your application doesn't handle
+ * disconnections.
+ * @param ma A pointer to an MQTTAsync_messageArrived() callback
+ * function. You can set this to NULL if your application doesn't handle
+ * receipt of messages.
+ * @param dc A pointer to an MQTTAsync_deliveryComplete() callback
+ * function. You can set this to NULL if you do not want to check
+ * for successful delivery.
+ * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
+ * ::MQTTASYNC_FAILURE if an error occurred.
+ */
+DLLExport int MQTTAsync_setCallbacks(MQTTAsync handle, void* context, MQTTAsync_connectionLost* cl,
+ MQTTAsync_messageArrived* ma, MQTTAsync_deliveryComplete* dc);
+
+
+/**
+ * Sets the MQTTAsync_connected() callback function for a client.
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param context A pointer to any application-specific context. The
+ * the <i>context</i> pointer is passed to each of the callback functions to
+ * provide access to the context information in the callback.
+ * @param co A pointer to an MQTTAsync_connected() callback
+ * function. NULL removes the callback setting.
+ * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
+ * ::MQTTASYNC_FAILURE if an error occurred.
+ */
+DLLExport int MQTTAsync_setConnected(MQTTAsync handle, void* context, MQTTAsync_connected* co);
+
+
+/**
+ * Reconnects a client with the previously used connect options. Connect
+ * must have previously been called for this to work.
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set,
+ * ::MQTTASYNC_FAILURE if an error occurred.
+ */
+DLLExport int MQTTAsync_reconnect(MQTTAsync handle);
+
+
+/**
+ * This function creates an MQTT client ready for connection to the
+ * specified server and using the specified persistent storage (see
+ * MQTTAsync_persistence). See also MQTTAsync_destroy().
+ * @param handle A pointer to an ::MQTTAsync handle. The handle is
+ * populated with a valid client reference following a successful return from
+ * this function.
+ * @param serverURI A null-terminated string specifying the server to
+ * which the client will connect. It takes the form <i>protocol://host:port</i>.
+ * <i>protocol</i> must be <i>tcp</i> or <i>ssl</i>. For <i>host</i>, you can
+ * specify either an IP address or a host name. For instance, to connect to
+ * a server running on the local machines with the default MQTT port, specify
+ * <i>tcp://localhost:1883</i>.
+ * @param clientId The client identifier passed to the server when the
+ * client connects to it. It is a null-terminated UTF-8 encoded string.
+ * @param persistence_type The type of persistence to be used by the client:
+ * <br>
+ * ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or
+ * system on which the client is running fails or is switched off, the current
+ * state of any in-flight messages is lost and some messages may not be
+ * delivered even at QoS1 and QoS2.
+ * <br>
+ * ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based)
+ * persistence mechanism. Status about in-flight messages is held in persistent
+ * storage and provides some protection against message loss in the case of
+ * unexpected failure.
+ * <br>
+ * ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence
+ * implementation. Using this type of persistence gives control of the
+ * persistence mechanism to the application. The application has to implement
+ * the MQTTClient_persistence interface.
+ * @param persistence_context If the application uses
+ * ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should
+ * be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it
+ * should be set to the location of the persistence directory (if set
+ * to NULL, the persistence directory used is the working directory).
+ * Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this
+ * argument to point to a valid MQTTClient_persistence structure.
+ * @return ::MQTTASYNC_SUCCESS if the client is successfully created, otherwise
+ * an error code is returned.
+ */
+DLLExport int MQTTAsync_create(MQTTAsync* handle, const char* serverURI, const char* clientId,
+ int persistence_type, void* persistence_context);
+
+typedef struct
+{
+ /** The eyecatcher for this structure. must be MQCO. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 */
+ int struct_version;
+ /** Whether to allow messages to be sent when the client library is not connected. */
+ int sendWhileDisconnected;
+ /** the maximum number of messages allowed to be buffered while not connected. */
+ int maxBufferedMessages;
+} MQTTAsync_createOptions;
+
+#define MQTTAsync_createOptions_initializer { {'M', 'Q', 'C', 'O'}, 0, 0, 100 }
+
+
+DLLExport int MQTTAsync_createWithOptions(MQTTAsync* handle, const char* serverURI, const char* clientId,
+ int persistence_type, void* persistence_context, MQTTAsync_createOptions* options);
+
+/**
+ * MQTTAsync_willOptions defines the MQTT "Last Will and Testament" (LWT) settings for
+ * the client. In the event that a client unexpectedly loses its connection to
+ * the server, the server publishes the LWT message to the LWT topic on
+ * behalf of the client. This allows other clients (subscribed to the LWT topic)
+ * to be made aware that the client has disconnected. To enable the LWT
+ * function for a specific client, a valid pointer to an MQTTAsync_willOptions
+ * structure is passed in the MQTTAsync_connectOptions structure used in the
+ * MQTTAsync_connect() call that connects the client to the server. The pointer
+ * to MQTTAsync_willOptions can be set to NULL if the LWT function is not
+ * required.
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. must be MQTW. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 or 1
+ 0 indicates no binary will message support
+ */
+ int struct_version;
+ /** The LWT topic to which the LWT message will be published. */
+ const char* topicName;
+ /** The LWT payload. */
+ const char* message;
+ /**
+ * The retained flag for the LWT message (see MQTTAsync_message.retained).
+ */
+ int retained;
+ /**
+ * The quality of service setting for the LWT message (see
+ * MQTTAsync_message.qos and @ref qos).
+ */
+ int qos;
+ /** The LWT payload in binary form. This is only checked and used if the message option is NULL */
+ struct
+ {
+ int len; /**< binary payload length */
+ const void* data; /**< binary payload data */
+ } payload;
+} MQTTAsync_willOptions;
+
+#define MQTTAsync_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 1, NULL, NULL, 0, 0, { 0, NULL } }
+
+#define MQTT_SSL_VERSION_DEFAULT 0
+#define MQTT_SSL_VERSION_TLS_1_0 1
+#define MQTT_SSL_VERSION_TLS_1_1 2
+#define MQTT_SSL_VERSION_TLS_1_2 3
+
+/**
+* MQTTAsync_sslProperties defines the settings to establish an SSL/TLS connection using the
+* OpenSSL library. It covers the following scenarios:
+* - Server authentication: The client needs the digital certificate of the server. It is included
+* in a store containting trusted material (also known as "trust store").
+* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In
+* addition to the digital certificate of the server in a trust store, the client will need its own
+* digital certificate and the private key used to sign its digital certificate stored in a "key store".
+* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed
+* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to
+* man-in-the-middle attacks.
+*/
+typedef struct
+{
+ /** The eyecatcher for this structure. Must be MQTS */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0, or 1 to enable TLS version selection. */
+ int struct_version;
+
+ /** The file in PEM format containing the public digital certificates trusted by the client. */
+ const char* trustStore;
+
+ /** The file in PEM format containing the public certificate chain of the client. It may also include
+ * the client's private key.
+ */
+ const char* keyStore;
+
+ /** If not included in the sslKeyStore, this setting points to the file in PEM format containing
+ * the client's private key.
+ */
+ const char* privateKey;
+ /** The password to load the client's privateKey if encrypted. */
+ const char* privateKeyPassword;
+
+ /**
+ * The list of cipher suites that the client will present to the server during the SSL handshake. For a
+ * full explanation of the cipher list format, please see the OpenSSL on-line documentation:
+ * http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
+ * If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding
+ * those offering no encryption- will be considered.
+ * This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance).
+ */
+ const char* enabledCipherSuites;
+
+ /** True/False option to enable verification of the server certificate **/
+ int enableServerCertAuth;
+
+ /** The SSL/TLS version to use. Specify one of MQTT_SSL_VERSION_DEFAULT (0),
+ * MQTT_SSL_VERSION_TLS_1_0 (1), MQTT_SSL_VERSION_TLS_1_1 (2) or MQTT_SSL_VERSION_TLS_1_2 (3).
+ * Only used if struct_version is >= 1.
+ */
+ int sslVersion;
+
+ /**
+ * Whether to carry out post-connect checks, including that a certificate
+ * matches the given host name.
+ * Exists only if struct_version >= 2
+ */
+ int verify;
+
+ /**
+ * From the OpenSSL documentation:
+ * If CApath is not NULL, it points to a directory containing CA certificates in PEM format.
+ * Exists only if struct_version >= 2
+ */
+ const char* CApath;
+} MQTTAsync_SSLOptions;
+
+#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 2, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL }
+
+/**
+ * MQTTAsync_connectOptions defines several settings that control the way the
+ * client connects to an MQTT server. Default values are set in
+ * MQTTAsync_connectOptions_initializer.
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. must be MQTC. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0, 1, 2, 3 4 or 5.
+ * 0 signifies no SSL options and no serverURIs
+ * 1 signifies no serverURIs
+ * 2 signifies no MQTTVersion
+ * 3 signifies no automatic reconnect options
+ * 4 signifies no binary password option (just string)
+ */
+ int struct_version;
+ /** The "keep alive" interval, measured in seconds, defines the maximum time
+ * that should pass without communication between the client and the server
+ * The client will ensure that at least one message travels across the
+ * network within each keep alive period. In the absence of a data-related
+ * message during the time period, the client sends a very small MQTT
+ * "ping" message, which the server will acknowledge. The keep alive
+ * interval enables the client to detect when the server is no longer
+ * available without having to wait for the long TCP/IP timeout.
+ * Set to 0 if you do not want any keep alive processing.
+ */
+ int keepAliveInterval;
+ /**
+ * This is a boolean value. The cleansession setting controls the behaviour
+ * of both the client and the server at connection and disconnection time.
+ * The client and server both maintain session state information. This
+ * information is used to ensure "at least once" and "exactly once"
+ * delivery, and "exactly once" receipt of messages. Session state also
+ * includes subscriptions created by an MQTT client. You can choose to
+ * maintain or discard state information between sessions.
+ *
+ * When cleansession is true, the state information is discarded at
+ * connect and disconnect. Setting cleansession to false keeps the state
+ * information. When you connect an MQTT client application with
+ * MQTTAsync_connect(), the client identifies the connection using the
+ * client identifier and the address of the server. The server checks
+ * whether session information for this client
+ * has been saved from a previous connection to the server. If a previous
+ * session still exists, and cleansession=true, then the previous session
+ * information at the client and server is cleared. If cleansession=false,
+ * the previous session is resumed. If no previous session exists, a new
+ * session is started.
+ */
+ int cleansession;
+ /**
+ * This controls how many messages can be in-flight simultaneously.
+ */
+ int maxInflight;
+ /**
+ * This is a pointer to an MQTTAsync_willOptions structure. If your
+ * application does not make use of the Last Will and Testament feature,
+ * set this pointer to NULL.
+ */
+ MQTTAsync_willOptions* will;
+ /**
+ * MQTT servers that support the MQTT v3.1 protocol provide authentication
+ * and authorisation by user name and password. This is the user name
+ * parameter.
+ */
+ const char* username;
+ /**
+ * MQTT servers that support the MQTT v3.1 protocol provide authentication
+ * and authorisation by user name and password. This is the password
+ * parameter.
+ */
+ const char* password;
+ /**
+ * The time interval in seconds to allow a connect to complete.
+ */
+ int connectTimeout;
+ /**
+ * The time interval in seconds
+ */
+ int retryInterval;
+ /**
+ * This is a pointer to an MQTTAsync_SSLOptions structure. If your
+ * application does not make use of SSL, set this pointer to NULL.
+ */
+ MQTTAsync_SSLOptions* ssl;
+ /**
+ * A pointer to a callback function to be called if the connect successfully
+ * completes. Can be set to NULL, in which case no indication of successful
+ * completion will be received.
+ */
+ MQTTAsync_onSuccess* onSuccess;
+ /**
+ * A pointer to a callback function to be called if the connect fails.
+ * Can be set to NULL, in which case no indication of unsuccessful
+ * completion will be received.
+ */
+ MQTTAsync_onFailure* onFailure;
+ /**
+ * A pointer to any application-specific context. The
+ * the <i>context</i> pointer is passed to success or failure callback functions to
+ * provide access to the context information in the callback.
+ */
+ void* context;
+ /**
+ * The number of entries in the serverURIs array.
+ */
+ int serverURIcount;
+ /**
+ * An array of null-terminated strings specifying the servers to
+ * which the client will connect. Each string takes the form <i>protocol://host:port</i>.
+ * <i>protocol</i> must be <i>tcp</i> or <i>ssl</i>. For <i>host</i>, you can
+ * specify either an IP address or a domain name. For instance, to connect to
+ * a server running on the local machines with the default MQTT port, specify
+ * <i>tcp://localhost:1883</i>.
+ */
+ char* const* serverURIs;
+ /**
+ * Sets the version of MQTT to be used on the connect.
+ * MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that fails, fall back to 3.1
+ * MQTTVERSION_3_1 (3) = only try version 3.1
+ * MQTTVERSION_3_1_1 (4) = only try version 3.1.1
+ */
+ int MQTTVersion;
+ /**
+ * Reconnect automatically in the case of a connection being lost?
+ */
+ int automaticReconnect;
+ /**
+ * Minimum retry interval in seconds. Doubled on each failed retry.
+ */
+ int minRetryInterval;
+ /**
+ * Maximum retry interval in seconds. The doubling stops here on failed retries.
+ */
+ int maxRetryInterval;
+ /**
+ * Optional binary password. Only checked and used if the password option is NULL
+ */
+ struct {
+ int len; /**< binary password length */
+ const void* data; /**< binary password data */
+ } binarypwd;
+} MQTTAsync_connectOptions;
+
+
+#define MQTTAsync_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 5, 60, 1, 10, NULL, NULL, NULL, 30, 0,\
+NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 1, 60, {0, NULL}}
+
+/**
+ * This function attempts to connect a previously-created client (see
+ * MQTTAsync_create()) to an MQTT server using the specified options. If you
+ * want to enable asynchronous message and status notifications, you must call
+ * MQTTAsync_setCallbacks() prior to MQTTAsync_connect().
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param options A pointer to a valid MQTTAsync_connectOptions
+ * structure.
+ * @return ::MQTTASYNC_SUCCESS if the client connect request was accepted.
+ * If the client was unable to connect to the server, an error code is
+ * returned via the onFailure callback, if set.
+ * Error codes greater than 0 are returned by the MQTT protocol:<br><br>
+ * <b>1</b>: Connection refused: Unacceptable protocol version<br>
+ * <b>2</b>: Connection refused: Identifier rejected<br>
+ * <b>3</b>: Connection refused: Server unavailable<br>
+ * <b>4</b>: Connection refused: Bad user name or password<br>
+ * <b>5</b>: Connection refused: Not authorized<br>
+ * <b>6-255</b>: Reserved for future use<br>
+ */
+DLLExport int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options);
+
+
+typedef struct
+{
+ /** The eyecatcher for this structure. Must be MQTD. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 or 1. 0 signifies no SSL options */
+ int struct_version;
+ /**
+ * The client delays disconnection for up to this time (in
+ * milliseconds) in order to allow in-flight message transfers to complete.
+ */
+ int timeout;
+ /**
+ * A pointer to a callback function to be called if the disconnect successfully
+ * completes. Can be set to NULL, in which case no indication of successful
+ * completion will be received.
+ */
+ MQTTAsync_onSuccess* onSuccess;
+ /**
+ * A pointer to a callback function to be called if the disconnect fails.
+ * Can be set to NULL, in which case no indication of unsuccessful
+ * completion will be received.
+ */
+ MQTTAsync_onFailure* onFailure;
+ /**
+ * A pointer to any application-specific context. The
+ * the <i>context</i> pointer is passed to success or failure callback functions to
+ * provide access to the context information in the callback.
+ */
+ void* context;
+} MQTTAsync_disconnectOptions;
+
+#define MQTTAsync_disconnectOptions_initializer { {'M', 'Q', 'T', 'D'}, 0, 0, NULL, NULL, NULL }
+
+
+/**
+ * This function attempts to disconnect the client from the MQTT
+ * server. In order to allow the client time to complete handling of messages
+ * that are in-flight when this function is called, a timeout period is
+ * specified. When the timeout period has expired, the client disconnects even
+ * if there are still outstanding message acknowledgements.
+ * The next time the client connects to the same server, any QoS 1 or 2
+ * messages which have not completed will be retried depending on the
+ * cleansession settings for both the previous and the new connection (see
+ * MQTTAsync_connectOptions.cleansession and MQTTAsync_connect()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param options The client delays disconnection for up to this time (in
+ * milliseconds) in order to allow in-flight message transfers to complete.
+ * @return ::MQTTASYNC_SUCCESS if the client successfully disconnects from
+ * the server. An error code is returned if the client was unable to disconnect
+ * from the server
+ */
+DLLExport int MQTTAsync_disconnect(MQTTAsync handle, const MQTTAsync_disconnectOptions* options);
+
+
+/**
+ * This function allows the client application to test whether or not a
+ * client is currently connected to the MQTT server.
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @return Boolean true if the client is connected, otherwise false.
+ */
+DLLExport int MQTTAsync_isConnected(MQTTAsync handle);
+
+
+/**
+ * This function attempts to subscribe a client to a single topic, which may
+ * contain wildcards (see @ref wildcard). This call also specifies the
+ * @ref qos requested for the subscription
+ * (see also MQTTAsync_subscribeMany()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param topic The subscription topic, which may include wildcards.
+ * @param qos The requested quality of service for the subscription.
+ * @param response A pointer to a response options structure. Used to set callback functions.
+ * @return ::MQTTASYNC_SUCCESS if the subscription request is successful.
+ * An error code is returned if there was a problem registering the
+ * subscription.
+ */
+DLLExport int MQTTAsync_subscribe(MQTTAsync handle, const char* topic, int qos, MQTTAsync_responseOptions* response);
+
+
+/**
+ * This function attempts to subscribe a client to a list of topics, which may
+ * contain wildcards (see @ref wildcard). This call also specifies the
+ * @ref qos requested for each topic (see also MQTTAsync_subscribe()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param count The number of topics for which the client is requesting
+ * subscriptions.
+ * @param topic An array (of length <i>count</i>) of pointers to
+ * topics, each of which may include wildcards.
+ * @param qos An array (of length <i>count</i>) of @ref qos
+ * values. qos[n] is the requested QoS for topic[n].
+ * @param response A pointer to a response options structure. Used to set callback functions.
+ * @return ::MQTTASYNC_SUCCESS if the subscription request is successful.
+ * An error code is returned if there was a problem registering the
+ * subscriptions.
+ */
+DLLExport int MQTTAsync_subscribeMany(MQTTAsync handle, int count, char* const* topic, int* qos, MQTTAsync_responseOptions* response);
+
+/**
+ * This function attempts to remove an existing subscription made by the
+ * specified client.
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param topic The topic for the subscription to be removed, which may
+ * include wildcards (see @ref wildcard).
+ * @param response A pointer to a response options structure. Used to set callback functions.
+ * @return ::MQTTASYNC_SUCCESS if the subscription is removed.
+ * An error code is returned if there was a problem removing the
+ * subscription.
+ */
+DLLExport int MQTTAsync_unsubscribe(MQTTAsync handle, const char* topic, MQTTAsync_responseOptions* response);
+
+/**
+ * This function attempts to remove existing subscriptions to a list of topics
+ * made by the specified client.
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param count The number subscriptions to be removed.
+ * @param topic An array (of length <i>count</i>) of pointers to the topics of
+ * the subscriptions to be removed, each of which may include wildcards.
+ * @param response A pointer to a response options structure. Used to set callback functions.
+ * @return ::MQTTASYNC_SUCCESS if the subscriptions are removed.
+ * An error code is returned if there was a problem removing the subscriptions.
+ */
+DLLExport int MQTTAsync_unsubscribeMany(MQTTAsync handle, int count, char* const* topic, MQTTAsync_responseOptions* response);
+
+
+/**
+ * This function attempts to publish a message to a given topic (see also
+ * ::MQTTAsync_sendMessage()). An ::MQTTAsync_token is issued when
+ * this function returns successfully. If the client application needs to
+ * test for successful delivery of messages, a callback should be set
+ * (see ::MQTTAsync_onSuccess() and ::MQTTAsync_deliveryComplete()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param destinationName The topic associated with this message.
+ * @param payloadlen The length of the payload in bytes.
+ * @param payload A pointer to the byte array payload of the message.
+ * @param qos The @ref qos of the message.
+ * @param retained The retained flag for the message.
+ * @param response A pointer to an ::MQTTAsync_responseOptions structure. Used to set callback functions.
+ * This is optional and can be set to NULL.
+ * @return ::MQTTASYNC_SUCCESS if the message is accepted for publication.
+ * An error code is returned if there was a problem accepting the message.
+ */
+DLLExport int MQTTAsync_send(MQTTAsync handle, const char* destinationName, int payloadlen, void* payload, int qos, int retained,
+ MQTTAsync_responseOptions* response);
+
+
+/**
+ * This function attempts to publish a message to a given topic (see also
+ * MQTTAsync_publish()). An ::MQTTAsync_token is issued when
+ * this function returns successfully. If the client application needs to
+ * test for successful delivery of messages, a callback should be set
+ * (see ::MQTTAsync_onSuccess() and ::MQTTAsync_deliveryComplete()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param destinationName The topic associated with this message.
+ * @param msg A pointer to a valid MQTTAsync_message structure containing
+ * the payload and attributes of the message to be published.
+ * @param response A pointer to an ::MQTTAsync_responseOptions structure. Used to set callback functions.
+ * @return ::MQTTASYNC_SUCCESS if the message is accepted for publication.
+ * An error code is returned if there was a problem accepting the message.
+ */
+DLLExport int MQTTAsync_sendMessage(MQTTAsync handle, const char* destinationName, const MQTTAsync_message* msg, MQTTAsync_responseOptions* response);
+
+
+/**
+ * This function sets a pointer to an array of tokens for
+ * messages that are currently in-flight (pending completion).
+ *
+ * <b>Important note:</b> The memory used to hold the array of tokens is
+ * malloc()'d in this function. The client application is responsible for
+ * freeing this memory when it is no longer required.
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param tokens The address of a pointer to an ::MQTTAsync_token.
+ * When the function returns successfully, the pointer is set to point to an
+ * array of tokens representing messages pending completion. The last member of
+ * the array is set to -1 to indicate there are no more tokens. If no tokens
+ * are pending, the pointer is set to NULL.
+ * @return ::MQTTASYNC_SUCCESS if the function returns successfully.
+ * An error code is returned if there was a problem obtaining the list of
+ * pending tokens.
+ */
+DLLExport int MQTTAsync_getPendingTokens(MQTTAsync handle, MQTTAsync_token **tokens);
+
+/**
+ * Tests whether a request corresponding to a token is complete.
+ *
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param token An ::MQTTAsync_token associated with a request.
+ * @return 1 if the request has been completed, 0 if not.
+ */
+#define MQTTASYNC_TRUE 1
+DLLExport int MQTTAsync_isComplete(MQTTAsync handle, MQTTAsync_token token);
+
+
+/**
+ * Waits for a request corresponding to a token to complete.
+ *
+ * @param handle A valid client handle from a successful call to
+ * MQTTAsync_create().
+ * @param token An ::MQTTAsync_token associated with a request.
+ * @param timeout the maximum time to wait for completion, in milliseconds
+ * @return ::MQTTASYNC_SUCCESS if the request has been completed in the time allocated,
+ * ::MQTTASYNC_FAILURE if not.
+ */
+DLLExport int MQTTAsync_waitForCompletion(MQTTAsync handle, MQTTAsync_token token, unsigned long timeout);
+
+
+/**
+ * This function frees memory allocated to an MQTT message, including the
+ * additional memory allocated to the message payload. The client application
+ * calls this function when the message has been fully processed. <b>Important
+ * note:</b> This function does not free the memory allocated to a message
+ * topic string. It is the responsibility of the client application to free
+ * this memory using the MQTTAsync_free() library function.
+ * @param msg The address of a pointer to the ::MQTTAsync_message structure
+ * to be freed.
+ */
+DLLExport void MQTTAsync_freeMessage(MQTTAsync_message** msg);
+
+/**
+ * This function frees memory allocated by the MQTT C client library, especially the
+ * topic name. This is needed on Windows when the client libary and application
+ * program have been compiled with different versions of the C compiler. It is
+ * thus good policy to always use this function when freeing any MQTT C client-
+ * allocated memory.
+ * @param ptr The pointer to the client library storage to be freed.
+ */
+DLLExport void MQTTAsync_free(void* ptr);
+
+/**
+ * This function frees the memory allocated to an MQTT client (see
+ * MQTTAsync_create()). It should be called when the client is no longer
+ * required.
+ * @param handle A pointer to the handle referring to the ::MQTTAsync
+ * structure to be freed.
+ */
+DLLExport void MQTTAsync_destroy(MQTTAsync* handle);
+
+
+
+enum MQTTASYNC_TRACE_LEVELS
+{
+ MQTTASYNC_TRACE_MAXIMUM = 1,
+ MQTTASYNC_TRACE_MEDIUM,
+ MQTTASYNC_TRACE_MINIMUM,
+ MQTTASYNC_TRACE_PROTOCOL,
+ MQTTASYNC_TRACE_ERROR,
+ MQTTASYNC_TRACE_SEVERE,
+ MQTTASYNC_TRACE_FATAL,
+};
+
+
+/**
+ * This function sets the level of trace information which will be
+ * returned in the trace callback.
+ * @param level the trace level required
+ */
+DLLExport void MQTTAsync_setTraceLevel(enum MQTTASYNC_TRACE_LEVELS level);
+
+
+/**
+ * This is a callback function prototype which must be implemented if you want
+ * to receive trace information.
+ * @param level the trace level of the message returned
+ * @param meesage the trace message. This is a pointer to a static buffer which
+ * will be overwritten on each call. You must copy the data if you want to keep
+ * it for later.
+ */
+typedef void MQTTAsync_traceCallback(enum MQTTASYNC_TRACE_LEVELS level, char* message);
+
+/**
+ * This function sets the trace callback if needed. If set to NULL,
+ * no trace information will be returned. The default trace level is
+ * MQTTASYNC_TRACE_MINIMUM.
+ * @param callback a pointer to the function which will handle the trace information
+ */
+DLLExport void MQTTAsync_setTraceCallback(MQTTAsync_traceCallback* callback);
+
+
+typedef struct
+{
+ const char* name;
+ const char* value;
+} MQTTAsync_nameValue;
+
+/**
+ * This function returns version information about the library.
+ * no trace information will be returned. The default trace level is
+ * MQTTASYNC_TRACE_MINIMUM
+ * @return an array of strings describing the library. The last entry is a NULL pointer.
+ */
+DLLExport MQTTAsync_nameValue* MQTTAsync_getVersionInfo(void);
+
+
+/**
+ * @cond MQTTAsync_main
+ * @page async Threading
+ * The client application runs on several threads.
+ * Processing of handshaking and maintaining
+ * the network connection is performed in the background.
+ * This API is thread safe: functions may be called by multiple application
+ * threads.
+ * Notifications of status and message reception are provided to the client
+ * application using callbacks registered with the library by the call to
+ * MQTTAsync_setCallbacks() (see MQTTAsync_messageArrived(),
+ * MQTTAsync_connectionLost() and MQTTAsync_deliveryComplete()).
+ * In addition, some functions allow success and failure callbacks to be set
+ * for individual requests, in the ::MQTTAsync_responseOptions structure. Applications
+ * can be written as a chain of callback functions. Note that it is a theoretically
+ * possible but unlikely event, that a success or failure callback could be called
+ * before function requesting the callback has returned. In this case the token
+ * delivered in the callback would not yet be known to the application program (see
+ * Race condition for MQTTAsync_token in MQTTAsync.c
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=444093)
+ *
+ * @page auto_reconnect Automatic Reconnect
+ * The ability for the client library to reconnect automatically in the event
+ * of a connection failure was added in 1.1. The connection lost callback
+ * allows a flexible response to the loss of a connection, so almost any
+ * behaviour can be implemented in that way. Automatic reconnect does have the
+ * advantage of being a little simpler to use.
+ *
+ * To switch on automatic reconnect, the connect options field
+ * automaticReconnect should be set to non-zero. The minimum and maximum times
+ * before the next connection attempt can also be set, the defaults being 1 and
+ * 60 seconds. At each failure to reconnect, the retry interval is doubled until
+ * the maximum value is reached, and there it stays until the connection is
+ * successfully re-established whereupon it is reset.
+ *
+ * When a reconnection attempt is successful, the ::MQTTAsync_connected callback
+ * function is invoked, if set by calling ::MQTTAsync_setConnected. This allows
+ * the application to take any actions needed, such as amending subscriptions.
+ *
+ * @page offline_publish Publish While Disconnected
+ * This feature was not originally available because with persistence enabled,
+ * messages could be stored locally without ever knowing if they could be sent.
+ * The client application could have created the client with an erroneous broker
+ * address or port for instance.
+ *
+ * To enable messages to be published when the application is disconnected
+ * ::MQTTAsync_createWithOptions must be used instead of ::MQTTAsync_create to
+ * create the client object. The ::createOptions field sendWhileDisconnected
+ * must be set to non-zero, and the maxBufferedMessages field set as required -
+ * the default being 100.
+ *
+ * ::MQTTAsync_getPendingTokens can be called to return the ids of the messages
+ * waiting to be sent, or for which the sending process has not completed.
+ *
+ * @page wildcard Subscription wildcards
+ * Every MQTT message includes a topic that classifies it. MQTT servers use
+ * topics to determine which subscribers should receive messages published to
+ * the server.
+ *
+ * Consider the server receiving messages from several environmental sensors.
+ * Each sensor publishes its measurement data as a message with an associated
+ * topic. Subscribing applications need to know which sensor originally
+ * published each received message. A unique topic is thus used to identify
+ * each sensor and measurement type. Topics such as SENSOR1TEMP,
+ * SENSOR1HUMIDITY, SENSOR2TEMP and so on achieve this but are not very
+ * flexible. If additional sensors are added to the system at a later date,
+ * subscribing applications must be modified to receive them.
+ *
+ * To provide more flexibility, MQTT supports a hierarchical topic namespace.
+ * This allows application designers to organize topics to simplify their
+ * management. Levels in the hierarchy are delimited by the '/' character,
+ * such as SENSOR/1/HUMIDITY. Publishers and subscribers use these
+ * hierarchical topics as already described.
+ *
+ * For subscriptions, two wildcard characters are supported:
+ * <ul>
+ * <li>A '#' character represents a complete sub-tree of the hierarchy and
+ * thus must be the last character in a subscription topic string, such as
+ * SENSOR/#. This will match any topic starting with SENSOR/, such as
+ * SENSOR/1/TEMP and SENSOR/2/HUMIDITY.</li>
+ * <li> A '+' character represents a single level of the hierarchy and is
+ * used between delimiters. For example, SENSOR/+/TEMP will match
+ * SENSOR/1/TEMP and SENSOR/2/TEMP.</li>
+ * </ul>
+ * Publishers are not allowed to use the wildcard characters in their topic
+ * names.
+ *
+ * Deciding on your topic hierarchy is an important step in your system design.
+ *
+ * @page qos Quality of service
+ * The MQTT protocol provides three qualities of service for delivering
+ * messages between clients and servers: "at most once", "at least once" and
+ * "exactly once".
+ *
+ * Quality of service (QoS) is an attribute of an individual message being
+ * published. An application sets the QoS for a specific message by setting the
+ * MQTTAsync_message.qos field to the required value.
+ *
+ * A subscribing client can set the maximum quality of service a server uses
+ * to send messages that match the client subscriptions. The
+ * MQTTAsync_subscribe() and MQTTAsync_subscribeMany() functions set this
+ * maximum. The QoS of a message forwarded to a subscriber thus might be
+ * different to the QoS given to the message by the original publisher.
+ * The lower of the two values is used to forward a message.
+ *
+ * The three levels are:
+ *
+ * <b>QoS0, At most once:</b> The message is delivered at most once, or it
+ * may not be delivered at all. Its delivery across the network is not
+ * acknowledged. The message is not stored. The message could be lost if the
+ * client is disconnected, or if the server fails. QoS0 is the fastest mode of
+ * transfer. It is sometimes called "fire and forget".
+ *
+ * The MQTT protocol does not require servers to forward publications at QoS0
+ * to a client. If the client is disconnected at the time the server receives
+ * the publication, the publication might be discarded, depending on the
+ * server implementation.
+ *
+ * <b>QoS1, At least once:</b> The message is always delivered at least once.
+ * It might be delivered multiple times if there is a failure before an
+ * acknowledgment is received by the sender. The message must be stored
+ * locally at the sender, until the sender receives confirmation that the
+ * message has been published by the receiver. The message is stored in case
+ * the message must be sent again.
+ *
+ * <b>QoS2, Exactly once:</b> The message is always delivered exactly once.
+ * The message must be stored locally at the sender, until the sender receives
+ * confirmation that the message has been published by the receiver. The
+ * message is stored in case the message must be sent again. QoS2 is the
+ * safest, but slowest mode of transfer. A more sophisticated handshaking
+ * and acknowledgement sequence is used than for QoS1 to ensure no duplication
+ * of messages occurs.
+
+
+ * @page publish Publication example
+@code
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "MQTTAsync.h"
+
+#define ADDRESS "tcp://localhost:1883"
+#define CLIENTID "ExampleClientPub"
+#define TOPIC "MQTT Examples"
+#define PAYLOAD "Hello World!"
+#define QOS 1
+#define TIMEOUT 10000L
+
+volatile MQTTAsync_token deliveredtoken;
+
+int finished = 0;
+
+void connlost(void *context, char *cause)
+{
+ MQTTAsync client = (MQTTAsync)context;
+ MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
+ int rc;
+
+ printf("\nConnection lost\n");
+ printf(" cause: %s\n", cause);
+
+ printf("Reconnecting\n");
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+ if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start connect, return code %d\n", rc);
+ finished = 1;
+ }
+}
+
+
+void onDisconnect(void* context, MQTTAsync_successData* response)
+{
+ printf("Successful disconnection\n");
+ finished = 1;
+}
+
+
+void onSend(void* context, MQTTAsync_successData* response)
+{
+ MQTTAsync client = (MQTTAsync)context;
+ MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
+ int rc;
+
+ printf("Message with token value %d delivery confirmed\n", response->token);
+
+ opts.onSuccess = onDisconnect;
+ opts.context = client;
+
+ if ((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start sendMessage, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void onConnectFailure(void* context, MQTTAsync_failureData* response)
+{
+ printf("Connect failed, rc %d\n", response ? response->code : 0);
+ finished = 1;
+}
+
+
+void onConnect(void* context, MQTTAsync_successData* response)
+{
+ MQTTAsync client = (MQTTAsync)context;
+ MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
+ MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
+ int rc;
+
+ printf("Successful connection\n");
+
+ opts.onSuccess = onSend;
+ opts.context = client;
+
+ pubmsg.payload = PAYLOAD;
+ pubmsg.payloadlen = strlen(PAYLOAD);
+ pubmsg.qos = QOS;
+ pubmsg.retained = 0;
+ deliveredtoken = 0;
+
+ if ((rc = MQTTAsync_sendMessage(client, TOPIC, &pubmsg, &opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start sendMessage, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ MQTTAsync client;
+ MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
+ MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
+ MQTTAsync_token token;
+ int rc;
+
+ MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
+
+ MQTTAsync_setCallbacks(client, NULL, connlost, NULL, NULL);
+
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+ conn_opts.onSuccess = onConnect;
+ conn_opts.onFailure = onConnectFailure;
+ conn_opts.context = client;
+ if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start connect, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Waiting for publication of %s\n"
+ "on topic %s for client with ClientID: %s\n",
+ PAYLOAD, TOPIC, CLIENTID);
+ while (!finished)
+ #if defined(WIN32) || defined(WIN64)
+ Sleep(100);
+ #else
+ usleep(10000L);
+ #endif
+
+ MQTTAsync_destroy(&client);
+ return rc;
+}
+
+ * @endcode
+ * @page subscribe Subscription example
+@code
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "MQTTAsync.h"
+
+#define ADDRESS "tcp://localhost:1883"
+#define CLIENTID "ExampleClientSub"
+#define TOPIC "MQTT Examples"
+#define PAYLOAD "Hello World!"
+#define QOS 1
+#define TIMEOUT 10000L
+
+volatile MQTTAsync_token deliveredtoken;
+
+int disc_finished = 0;
+int subscribed = 0;
+int finished = 0;
+
+void connlost(void *context, char *cause)
+{
+ MQTTAsync client = (MQTTAsync)context;
+ MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
+ int rc;
+
+ printf("\nConnection lost\n");
+ printf(" cause: %s\n", cause);
+
+ printf("Reconnecting\n");
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+ if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start connect, return code %d\n", rc);
+ finished = 1;
+ }
+}
+
+
+int msgarrvd(void *context, char *topicName, int topicLen, MQTTAsync_message *message)
+{
+ int i;
+ char* payloadptr;
+
+ printf("Message arrived\n");
+ printf(" topic: %s\n", topicName);
+ printf(" message: ");
+
+ payloadptr = message->payload;
+ for(i=0; i<message->payloadlen; i++)
+ {
+ putchar(*payloadptr++);
+ }
+ putchar('\n');
+ MQTTAsync_freeMessage(&message);
+ MQTTAsync_free(topicName);
+ return 1;
+}
+
+
+void onDisconnect(void* context, MQTTAsync_successData* response)
+{
+ printf("Successful disconnection\n");
+ disc_finished = 1;
+}
+
+
+void onSubscribe(void* context, MQTTAsync_successData* response)
+{
+ printf("Subscribe succeeded\n");
+ subscribed = 1;
+}
+
+void onSubscribeFailure(void* context, MQTTAsync_failureData* response)
+{
+ printf("Subscribe failed, rc %d\n", response ? response->code : 0);
+ finished = 1;
+}
+
+
+void onConnectFailure(void* context, MQTTAsync_failureData* response)
+{
+ printf("Connect failed, rc %d\n", response ? response->code : 0);
+ finished = 1;
+}
+
+
+void onConnect(void* context, MQTTAsync_successData* response)
+{
+ MQTTAsync client = (MQTTAsync)context;
+ MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
+ MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
+ int rc;
+
+ printf("Successful connection\n");
+
+ printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
+ "Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
+ opts.onSuccess = onSubscribe;
+ opts.onFailure = onSubscribeFailure;
+ opts.context = client;
+
+ deliveredtoken = 0;
+
+ if ((rc = MQTTAsync_subscribe(client, TOPIC, QOS, &opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start subscribe, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ MQTTAsync client;
+ MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
+ MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer;
+ MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
+ MQTTAsync_token token;
+ int rc;
+ int ch;
+
+ MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
+
+ MQTTAsync_setCallbacks(client, NULL, connlost, msgarrvd, NULL);
+
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+ conn_opts.onSuccess = onConnect;
+ conn_opts.onFailure = onConnectFailure;
+ conn_opts.context = client;
+ if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start connect, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ while (!subscribed)
+ #if defined(WIN32) || defined(WIN64)
+ Sleep(100);
+ #else
+ usleep(10000L);
+ #endif
+
+ if (finished)
+ goto exit;
+
+ do
+ {
+ ch = getchar();
+ } while (ch!='Q' && ch != 'q');
+
+ disc_opts.onSuccess = onDisconnect;
+ if ((rc = MQTTAsync_disconnect(client, &disc_opts)) != MQTTASYNC_SUCCESS)
+ {
+ printf("Failed to start disconnect, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+ while (!disc_finished)
+ #if defined(WIN32) || defined(WIN64)
+ Sleep(100);
+ #else
+ usleep(10000L);
+ #endif
+
+exit:
+ MQTTAsync_destroy(&client);
+ return rc;
+}
+
+ * @endcode
+* @page tracing Tracing
+ *
+ * Runtime tracing can be controlled by environment variables or API calls.
+ *
+ * #### Environment variables
+ *
+ * Tracing is switched on by setting the MQTT_C_CLIENT_TRACE environment variable.
+ * A value of ON, or stdout, prints to stdout, any other value is interpreted as a file name to use.
+ *
+ * The amount of trace detail is controlled with the MQTT_C_CLIENT_TRACE_LEVEL environment
+ * variable - valid values are ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM
+ * (from least to most verbose).
+ *
+ * The variable MQTT_C_CLIENT_TRACE_MAX_LINES limits the number of lines of trace that are output
+ * to a file. Two files are used at most, when they are full, the last one is overwritten with the
+ * new trace entries. The default size is 1000 lines.
+ *
+ * #### Trace API calls
+ *
+ * MQTTAsync_traceCallback() is used to set a callback function which is called whenever trace
+ * information is available. This will be the same information as that printed if the
+ * environment variables were used to control the trace.
+ *
+ * The MQTTAsync_setTraceLevel() calls is used to set the maximum level of trace entries that will be
+ * passed to the callback function. The levels are:
+ * 1. ::MQTTASYNC_TRACE_MAXIMUM
+ * 2. ::MQTTASYNC_TRACE_MEDIUM
+ * 3. ::MQTTASYNC_TRACE_MINIMUM
+ * 4. ::MQTTASYNC_TRACE_PROTOCOL
+ * 5. ::MQTTASYNC_TRACE_ERROR
+ * 6. ::MQTTASYNC_TRACE_SEVERE
+ * 7. ::MQTTASYNC_TRACE_FATAL
+ *
+ * Selecting ::MQTTASYNC_TRACE_MAXIMUM will cause all trace entries at all levels to be returned.
+ * Choosing ::MQTTASYNC_TRACE_ERROR will cause ERROR, SEVERE and FATAL trace entries to be returned
+ * to the callback function.
+ *
+ * ### MQTT Packet Tracing
+ *
+ * A feature that can be very useful is printing the MQTT packets that are sent and received. To
+ * achieve this, use the following environment variable settings:
+ * @code
+ MQTT_C_CLIENT_TRACE=ON
+ MQTT_C_CLIENT_TRACE_LEVEL=PROTOCOL
+ * @endcode
+ * The output you should see looks like this:
+ * @code
+ 20130528 155936.813 3 stdout-subscriber -> CONNECT cleansession: 1 (0)
+ 20130528 155936.813 3 stdout-subscriber <- CONNACK rc: 0
+ 20130528 155936.813 3 stdout-subscriber -> SUBSCRIBE msgid: 1 (0)
+ 20130528 155936.813 3 stdout-subscriber <- SUBACK msgid: 1
+ 20130528 155941.818 3 stdout-subscriber -> DISCONNECT (0)
+ * @endcode
+ * where the fields are:
+ * 1. date
+ * 2. time
+ * 3. socket number
+ * 4. client id
+ * 5. direction (-> from client to server, <- from server to client)
+ * 6. packet details
+ *
+ * ### Default Level Tracing
+ *
+ * This is an extract of a default level trace of a call to connect:
+ * @code
+ 19700101 010000.000 (1152206656) (0)> MQTTClient_connect:893
+ 19700101 010000.000 (1152206656) (1)> MQTTClient_connectURI:716
+ 20130528 160447.479 Connecting to serverURI localhost:1883
+ 20130528 160447.479 (1152206656) (2)> MQTTProtocol_connect:98
+ 20130528 160447.479 (1152206656) (3)> MQTTProtocol_addressPort:48
+ 20130528 160447.479 (1152206656) (3)< MQTTProtocol_addressPort:73
+ 20130528 160447.479 (1152206656) (3)> Socket_new:599
+ 20130528 160447.479 New socket 4 for localhost, port 1883
+ 20130528 160447.479 (1152206656) (4)> Socket_addSocket:163
+ 20130528 160447.479 (1152206656) (5)> Socket_setnonblocking:73
+ 20130528 160447.479 (1152206656) (5)< Socket_setnonblocking:78 (0)
+ 20130528 160447.479 (1152206656) (4)< Socket_addSocket:176 (0)
+ 20130528 160447.479 (1152206656) (4)> Socket_error:95
+ 20130528 160447.479 (1152206656) (4)< Socket_error:104 (115)
+ 20130528 160447.479 Connect pending
+ 20130528 160447.479 (1152206656) (3)< Socket_new:683 (115)
+ 20130528 160447.479 (1152206656) (2)< MQTTProtocol_connect:131 (115)
+ * @endcode
+ * where the fields are:
+ * 1. date
+ * 2. time
+ * 3. thread id
+ * 4. function nesting level
+ * 5. function entry (>) or exit (<)
+ * 6. function name : line of source code file
+ * 7. return value (if there is one)
+ *
+ * ### Memory Allocation Tracing
+ *
+ * Setting the trace level to maximum causes memory allocations and frees to be traced along with
+ * the default trace entries, with messages like the following:
+ * @code
+ 20130528 161819.657 Allocating 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 177 ptr 0x179f930
+
+ 20130528 161819.657 Freeing 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 201, heap use now 896 bytes
+ * @endcode
+ * When the last MQTT client object is destroyed, if the trace is being recorded
+ * and all memory allocated by the client library has not been freed, an error message will be
+ * written to the trace. This can help with fixing memory leaks. The message will look like this:
+ * @code
+ 20130528 163909.208 Some memory not freed at shutdown, possible memory leak
+ 20130528 163909.208 Heap scan start, total 880 bytes
+ 20130528 163909.208 Heap element size 32, line 354, file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c, ptr 0x260cb00
+ 20130528 163909.208 Content
+ 20130528 163909.209 Heap scan end
+ * @endcode
+ * @endcond
+ */
+
+
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTClient.c b/tts-mqtt-test/src/paho-mqtt/MQTTClient.c
new file mode 100644
index 0000000..9b6fed9
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTClient.c
@@ -0,0 +1,2146 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - bug 384016 - segv setting will message
+ * Ian Craggs - bug 384053 - v1.0.0.7 - stop MQTTClient_receive on socket error
+ * Ian Craggs, Allan Stockdill-Mander - add ability to connect with SSL
+ * Ian Craggs - multiple server connection support
+ * Ian Craggs - fix for bug 413429 - connectionLost not called
+ * Ian Craggs - fix for bug 421103 - trying to write to same socket, in publish/retries
+ * Ian Craggs - fix for bug 419233 - mutexes not reporting errors
+ * Ian Craggs - fix for bug 420851
+ * Ian Craggs - fix for bug 432903 - queue persistence
+ * Ian Craggs - MQTT 3.1.1 support
+ * Ian Craggs - fix for bug 438176 - MQTT version selection
+ * Rong Xiang, Ian Craggs - C++ compatibility
+ * Ian Craggs - fix for bug 443724 - stack corruption
+ * Ian Craggs - fix for bug 447672 - simultaneous access to socket structure
+ * Ian Craggs - fix for bug 459791 - deadlock in WaitForCompletion for bad client
+ * Ian Craggs - fix for bug 474905 - insufficient synchronization for subscribe, unsubscribe, connect
+ * Ian Craggs - make it clear that yield and receive are not intended for multi-threaded mode (bug 474748)
+ * Ian Craggs - SNI support, message queue unpersist bug
+ * Ian Craggs - binary will message support
+ * Ian Craggs - waitforCompletion fix #240
+ * Ian Craggs - check for NULL SSL options #334
+ * Ian Craggs - allocate username/password buffers #431
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Synchronous API implementation
+ *
+ */
+
+#define _GNU_SOURCE /* for pthread_mutexattr_settype */
+#include <stdlib.h>
+#include <string.h>
+#if !defined(WIN32) && !defined(WIN64)
+ #include <sys/time.h>
+#endif
+
+#include "../paho-mqtt/MQTTClient.h"
+#if !defined(NO_PERSISTENCE)
+#include "../paho-mqtt/MQTTPersistence.h"
+#endif
+
+#include "../paho-mqtt/utf-8.h"
+#include "../paho-mqtt/MQTTProtocol.h"
+#include "../paho-mqtt/MQTTProtocolOut.h"
+#include "../paho-mqtt/Thread.h"
+#include "../paho-mqtt/SocketBuffer.h"
+#include "../paho-mqtt/StackTrace.h"
+#include "../paho-mqtt/Heap.h"
+
+#if defined(OPENSSL)
+#include <openssl/ssl.h>
+#else
+#define URI_SSL "ssl://"
+#endif
+
+#include "../paho-mqtt/OsWrapper.h"
+
+#define URI_TCP "tcp://"
+
+#include "../paho-mqtt/VersionInfo.h"
+
+
+const char *client_timestamp_eye = "MQTTClientV3_Timestamp " BUILD_TIMESTAMP;
+const char *client_version_eye = "MQTTClientV3_Version " CLIENT_VERSION;
+
+void MQTTClient_global_init(MQTTClient_init_options* inits)
+{
+#if defined(OPENSSL)
+ SSLSocket_handleOpensslInit(inits->do_openssl_init);
+#endif
+}
+
+static ClientStates ClientState =
+{
+ CLIENT_VERSION, /* version */
+ NULL /* client list */
+};
+
+ClientStates* bstate = &ClientState;
+
+MQTTProtocol state;
+
+#if defined(WIN32) || defined(WIN64)
+static mutex_type mqttclient_mutex = NULL;
+static mutex_type socket_mutex = NULL;
+static mutex_type subscribe_mutex = NULL;
+static mutex_type unsubscribe_mutex = NULL;
+static mutex_type connect_mutex = NULL;
+extern mutex_type stack_mutex;
+extern mutex_type heap_mutex;
+extern mutex_type log_mutex;
+BOOL APIENTRY DllMain(HANDLE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ Log(TRACE_MAX, -1, "DLL process attach");
+ if (mqttclient_mutex == NULL)
+ {
+ mqttclient_mutex = CreateMutex(NULL, 0, NULL);
+ subscribe_mutex = CreateMutex(NULL, 0, NULL);
+ unsubscribe_mutex = CreateMutex(NULL, 0, NULL);
+ connect_mutex = CreateMutex(NULL, 0, NULL);
+ stack_mutex = CreateMutex(NULL, 0, NULL);
+ heap_mutex = CreateMutex(NULL, 0, NULL);
+ log_mutex = CreateMutex(NULL, 0, NULL);
+ socket_mutex = CreateMutex(NULL, 0, NULL);
+ }
+ case DLL_THREAD_ATTACH:
+ Log(TRACE_MAX, -1, "DLL thread attach");
+ case DLL_THREAD_DETACH:
+ Log(TRACE_MAX, -1, "DLL thread detach");
+ case DLL_PROCESS_DETACH:
+ Log(TRACE_MAX, -1, "DLL process detach");
+ }
+ return TRUE;
+}
+#else
+static pthread_mutex_t mqttclient_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type mqttclient_mutex = &mqttclient_mutex_store;
+
+static pthread_mutex_t socket_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type socket_mutex = &socket_mutex_store;
+
+static pthread_mutex_t subscribe_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type subscribe_mutex = &subscribe_mutex_store;
+
+static pthread_mutex_t unsubscribe_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type unsubscribe_mutex = &unsubscribe_mutex_store;
+
+static pthread_mutex_t connect_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type connect_mutex = &connect_mutex_store;
+
+void MQTTClient_init(void)
+{
+ pthread_mutexattr_t attr;
+ int rc;
+
+ pthread_mutexattr_init(&attr);
+#if !defined(_WRS_KERNEL)
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#else
+ /* #warning "no pthread_mutexattr_settype" */
+#endif /* !defined(_WRS_KERNEL) */
+ if ((rc = pthread_mutex_init(mqttclient_mutex, &attr)) != 0)
+ printf("MQTTClient: error %d initializing client_mutex\n", rc);
+ if ((rc = pthread_mutex_init(socket_mutex, &attr)) != 0)
+ printf("MQTTClient: error %d initializing socket_mutex\n", rc);
+ if ((rc = pthread_mutex_init(subscribe_mutex, &attr)) != 0)
+ printf("MQTTClient: error %d initializing subscribe_mutex\n", rc);
+ if ((rc = pthread_mutex_init(unsubscribe_mutex, &attr)) != 0)
+ printf("MQTTClient: error %d initializing unsubscribe_mutex\n", rc);
+ if ((rc = pthread_mutex_init(connect_mutex, &attr)) != 0)
+ printf("MQTTClient: error %d initializing connect_mutex\n", rc);
+}
+
+#define WINAPI
+#endif
+
+static volatile int initialized = 0;
+static List* handles = NULL;
+static int running = 0;
+static int tostop = 0;
+static thread_id_type run_id = 0;
+
+typedef struct
+{
+ MQTTClient_message* msg;
+ char* topicName;
+ int topicLen;
+ unsigned int seqno; /* only used on restore */
+} qEntry;
+
+
+typedef struct
+{
+ char* serverURI;
+#if defined(OPENSSL)
+ int ssl;
+#endif
+ Clients* c;
+ MQTTClient_connectionLost* cl;
+ MQTTClient_messageArrived* ma;
+ MQTTClient_deliveryComplete* dc;
+ void* context;
+
+ sem_type connect_sem;
+ int rc; /* getsockopt return code in connect */
+ sem_type connack_sem;
+ sem_type suback_sem;
+ sem_type unsuback_sem;
+ MQTTPacket* pack;
+
+} MQTTClients;
+
+void MQTTClient_sleep(long milliseconds)
+{
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ Sleep(milliseconds);
+#else
+ usleep(milliseconds*1000);
+#endif
+ FUNC_EXIT;
+}
+
+
+#if defined(WIN32) || defined(WIN64)
+#define START_TIME_TYPE DWORD
+START_TIME_TYPE MQTTClient_start_clock(void)
+{
+ return GetTickCount();
+}
+#elif defined(AIX)
+#define START_TIME_TYPE struct timespec
+START_TIME_TYPE MQTTClient_start_clock(void)
+{
+ static struct timespec start;
+ clock_gettime(CLOCK_REALTIME, &start);
+ return start;
+}
+#else
+#define START_TIME_TYPE struct timeval
+START_TIME_TYPE MQTTClient_start_clock(void)
+{
+ static struct timeval start;
+ gettimeofday(&start, NULL);
+ return start;
+}
+#endif
+
+
+#if defined(WIN32) || defined(WIN64)
+long MQTTClient_elapsed(DWORD milliseconds)
+{
+ return GetTickCount() - milliseconds;
+}
+#elif defined(AIX)
+#define assert(a)
+long MQTTClient_elapsed(struct timespec start)
+{
+ struct timespec now, res;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ ntimersub(now, start, res);
+ return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L;
+}
+#else
+long MQTTClient_elapsed(struct timeval start)
+{
+ struct timeval now, res;
+
+ gettimeofday(&now, NULL);
+ timersub(&now, &start, &res);
+ return (res.tv_sec)*1000 + (res.tv_usec)/1000;
+}
+#endif
+
+static void MQTTClient_terminate(void);
+static void MQTTClient_emptyMessageQueue(Clients* client);
+static int MQTTClient_deliverMessage(
+ int rc, MQTTClients* m,
+ char** topicName, int* topicLen,
+ MQTTClient_message** message);
+static int clientSockCompare(void* a, void* b);
+static thread_return_type WINAPI connectionLost_call(void* context);
+static thread_return_type WINAPI MQTTClient_run(void* n);
+static void MQTTClient_stop(void);
+static void MQTTClient_closeSession(Clients* client);
+static int MQTTClient_cleanSession(Clients* client);
+static int MQTTClient_connectURIVersion(
+ MQTTClient handle, MQTTClient_connectOptions* options,
+ const char* serverURI, int MQTTVersion,
+ START_TIME_TYPE start, long millisecsTimeout);
+static int MQTTClient_connectURI(MQTTClient handle, MQTTClient_connectOptions* options, const char* serverURI);
+static int MQTTClient_disconnect1(MQTTClient handle, int timeout, int internal, int stop);
+static int MQTTClient_disconnect_internal(MQTTClient handle, int timeout);
+static void MQTTClient_retry(void);
+static MQTTPacket* MQTTClient_cycle(int* sock, unsigned long timeout, int* rc);
+static MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, long timeout);
+/*static int pubCompare(void* a, void* b); */
+static void MQTTProtocol_checkPendingWrites(void);
+static void MQTTClient_writeComplete(int socket, int rc);
+
+
+int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId,
+ int persistence_type, void* persistence_context)
+{
+ int rc = 0;
+ MQTTClients *m = NULL;
+
+ FUNC_ENTRY;
+ rc = Thread_lock_mutex(mqttclient_mutex);
+
+ if (serverURI == NULL || clientId == NULL)
+ {
+ rc = MQTTCLIENT_NULL_PARAMETER;
+ goto exit;
+ }
+
+ if (!UTF8_validateString(clientId))
+ {
+ rc = MQTTCLIENT_BAD_UTF8_STRING;
+ goto exit;
+ }
+
+ if (strstr(serverURI, "://") != NULL)
+ {
+ if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) != 0
+#if defined(OPENSSL)
+ && strncmp(URI_SSL, serverURI, strlen(URI_SSL)) != 0
+
+#endif
+ )
+ {
+ rc = MQTTCLIENT_BAD_PROTOCOL;
+ goto exit;
+ }
+ }
+
+ if (!initialized)
+ {
+ #if defined(HEAP_H)
+ Heap_initialize();
+ #endif
+ Log_initialize((Log_nameValue*)MQTTClient_getVersionInfo());
+ bstate->clients = ListInitialize();
+ Socket_outInitialize();
+ Socket_setWriteCompleteCallback(MQTTClient_writeComplete);
+ handles = ListInitialize();
+#if defined(OPENSSL)
+ SSLSocket_initialize();
+#endif
+ initialized = 1;
+ }
+ m = malloc(sizeof(MQTTClients));
+ *handle = m;
+ memset(m, '\0', sizeof(MQTTClients));
+ if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
+ serverURI += strlen(URI_TCP);
+ else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
+ {
+#if defined(OPENSSL)
+ serverURI += strlen(URI_SSL);
+ m->ssl = 1;
+#else
+ rc = MQTTCLIENT_SSL_NOT_SUPPORTED;
+ goto exit;
+#endif
+ }
+ m->serverURI = MQTTStrdup(serverURI);
+ ListAppend(handles, m, sizeof(MQTTClients));
+
+ m->c = malloc(sizeof(Clients));
+ memset(m->c, '\0', sizeof(Clients));
+ m->c->context = m;
+ m->c->outboundMsgs = ListInitialize();
+ m->c->inboundMsgs = ListInitialize();
+ m->c->messageQueue = ListInitialize();
+ m->c->clientID = MQTTStrdup(clientId);
+ m->connect_sem = Thread_create_sem();
+ m->connack_sem = Thread_create_sem();
+ m->suback_sem = Thread_create_sem();
+ m->unsuback_sem = Thread_create_sem();
+
+#if !defined(NO_PERSISTENCE)
+ rc = MQTTPersistence_create(&(m->c->persistence), persistence_type, persistence_context);
+ if (rc == 0)
+ {
+ rc = MQTTPersistence_initialize(m->c, m->serverURI);
+ if (rc == 0)
+ MQTTPersistence_restoreMessageQueue(m->c);
+ }
+#endif
+ ListAppend(bstate->clients, m->c, sizeof(Clients) + 3*sizeof(List));
+
+exit:
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void MQTTClient_terminate(void)
+{
+ FUNC_ENTRY;
+ MQTTClient_stop();
+ if (initialized)
+ {
+ ListFree(bstate->clients);
+ ListFree(handles);
+ handles = NULL;
+ Socket_outTerminate();
+#if defined(OPENSSL)
+ SSLSocket_terminate();
+#endif
+ #if defined(HEAP_H)
+ Heap_terminate();
+ #endif
+ Log_terminate();
+ initialized = 0;
+ }
+ FUNC_EXIT;
+}
+
+
+static void MQTTClient_emptyMessageQueue(Clients* client)
+{
+ FUNC_ENTRY;
+ /* empty message queue */
+ if (client->messageQueue->count > 0)
+ {
+ ListElement* current = NULL;
+ while (ListNextElement(client->messageQueue, &current))
+ {
+ qEntry* qe = (qEntry*)(current->content);
+ free(qe->topicName);
+ free(qe->msg->payload);
+ free(qe->msg);
+ }
+ ListEmpty(client->messageQueue);
+ }
+ FUNC_EXIT;
+}
+
+
+void MQTTClient_destroy(MQTTClient* handle)
+{
+ MQTTClients* m = *handle;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (m == NULL)
+ goto exit;
+
+ if (m->c)
+ {
+ int saved_socket = m->c->net.socket;
+ char* saved_clientid = MQTTStrdup(m->c->clientID);
+#if !defined(NO_PERSISTENCE)
+ MQTTPersistence_close(m->c);
+#endif
+ MQTTClient_emptyMessageQueue(m->c);
+ MQTTProtocol_freeClient(m->c);
+ if (!ListRemove(bstate->clients, m->c))
+ Log(LOG_ERROR, 0, NULL);
+ else
+ Log(TRACE_MIN, 1, NULL, saved_clientid, saved_socket);
+ free(saved_clientid);
+ }
+ if (m->serverURI)
+ free(m->serverURI);
+ Thread_destroy_sem(m->connect_sem);
+ Thread_destroy_sem(m->connack_sem);
+ Thread_destroy_sem(m->suback_sem);
+ Thread_destroy_sem(m->unsuback_sem);
+ if (!ListRemove(handles, m))
+ Log(LOG_ERROR, -1, "free error");
+ *handle = NULL;
+ if (bstate->clients->count == 0)
+ MQTTClient_terminate();
+
+exit:
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT;
+}
+
+
+void MQTTClient_freeMessage(MQTTClient_message** message)
+{
+ FUNC_ENTRY;
+ free((*message)->payload);
+ free(*message);
+ *message = NULL;
+ FUNC_EXIT;
+}
+
+
+void MQTTClient_free(void* memory)
+{
+ FUNC_ENTRY;
+ free(memory);
+ FUNC_EXIT;
+}
+
+
+static int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* topicLen, MQTTClient_message** message)
+{
+ qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
+
+ FUNC_ENTRY;
+ *message = qe->msg;
+ *topicName = qe->topicName;
+ *topicLen = qe->topicLen;
+ if (strlen(*topicName) != *topicLen)
+ rc = MQTTCLIENT_TOPICNAME_TRUNCATED;
+#if !defined(NO_PERSISTENCE)
+ if (m->c->persistence)
+ MQTTPersistence_unpersistQueueEntry(m->c, (MQTTPersistence_qEntry*)qe);
+#endif
+ ListRemove(m->c->messageQueue, m->c->messageQueue->first->content);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * List callback function for comparing clients by socket
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+static int clientSockCompare(void* a, void* b)
+{
+ MQTTClients* m = (MQTTClients*)a;
+ return m->c->net.socket == *(int*)b;
+}
+
+
+/**
+ * Wrapper function to call connection lost on a separate thread. A separate thread is needed to allow the
+ * connectionLost function to make API calls (e.g. connect)
+ * @param context a pointer to the relevant client
+ * @return thread_return_type standard thread return value - not used here
+ */
+static thread_return_type WINAPI connectionLost_call(void* context)
+{
+ MQTTClients* m = (MQTTClients*)context;
+
+ (*(m->cl))(m->context, NULL);
+ return 0;
+}
+
+
+/* This is the thread function that handles the calling of callback functions if set */
+static thread_return_type WINAPI MQTTClient_run(void* n)
+{
+ long timeout = 10L; /* first time in we have a small timeout. Gets things started more quickly */
+
+ FUNC_ENTRY;
+ running = 1;
+ run_id = Thread_getid();
+
+ Thread_lock_mutex(mqttclient_mutex);
+ while (!tostop)
+ {
+ int rc = SOCKET_ERROR;
+ int sock = -1;
+ MQTTClients* m = NULL;
+ MQTTPacket* pack = NULL;
+
+ Thread_unlock_mutex(mqttclient_mutex);
+ pack = MQTTClient_cycle(&sock, timeout, &rc);
+ Thread_lock_mutex(mqttclient_mutex);
+ if (tostop)
+ break;
+ timeout = 1000L;
+
+ /* find client corresponding to socket */
+ if (ListFindItem(handles, &sock, clientSockCompare) == NULL)
+ {
+ /* assert: should not happen */
+ continue;
+ }
+ m = (MQTTClient)(handles->current->content);
+ if (m == NULL)
+ {
+ /* assert: should not happen */
+ continue;
+ }
+ if (rc == SOCKET_ERROR)
+ {
+ if (m->c->connected)
+ MQTTClient_disconnect_internal(m, 0);
+ else
+ {
+ if (m->c->connect_state == 2 && !Thread_check_sem(m->connect_sem))
+ {
+ Log(TRACE_MIN, -1, "Posting connect semaphore for client %s", m->c->clientID);
+ Thread_post_sem(m->connect_sem);
+ }
+ if (m->c->connect_state == 3 && !Thread_check_sem(m->connack_sem))
+ {
+ Log(TRACE_MIN, -1, "Posting connack semaphore for client %s", m->c->clientID);
+ Thread_post_sem(m->connack_sem);
+ }
+ }
+ }
+ else
+ {
+ if (m->c->messageQueue->count > 0)
+ {
+ qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
+ int topicLen = qe->topicLen;
+
+ if (strlen(qe->topicName) == topicLen)
+ topicLen = 0;
+
+ Log(TRACE_MIN, -1, "Calling messageArrived for client %s, queue depth %d",
+ m->c->clientID, m->c->messageQueue->count);
+ Thread_unlock_mutex(mqttclient_mutex);
+ rc = (*(m->ma))(m->context, qe->topicName, topicLen, qe->msg);
+ Thread_lock_mutex(mqttclient_mutex);
+ /* if 0 (false) is returned by the callback then it failed, so we don't remove the message from
+ * the queue, and it will be retried later. If 1 is returned then the message data may have been freed,
+ * so we must be careful how we use it.
+ */
+ if (rc)
+ {
+ #if !defined(NO_PERSISTENCE)
+ if (m->c->persistence)
+ MQTTPersistence_unpersistQueueEntry(m->c, (MQTTPersistence_qEntry*)qe);
+ #endif
+ ListRemove(m->c->messageQueue, qe);
+ }
+ else
+ Log(TRACE_MIN, -1, "False returned from messageArrived for client %s, message remains on queue",
+ m->c->clientID);
+ }
+ if (pack)
+ {
+ if (pack->header.bits.type == CONNACK && !Thread_check_sem(m->connack_sem))
+ {
+ Log(TRACE_MIN, -1, "Posting connack semaphore for client %s", m->c->clientID);
+ m->pack = pack;
+ Thread_post_sem(m->connack_sem);
+ }
+ else if (pack->header.bits.type == SUBACK)
+ {
+ Log(TRACE_MIN, -1, "Posting suback semaphore for client %s", m->c->clientID);
+ m->pack = pack;
+ Thread_post_sem(m->suback_sem);
+ }
+ else if (pack->header.bits.type == UNSUBACK)
+ {
+ Log(TRACE_MIN, -1, "Posting unsuback semaphore for client %s", m->c->clientID);
+ m->pack = pack;
+ Thread_post_sem(m->unsuback_sem);
+ }
+ }
+ else if (m->c->connect_state == 1 && !Thread_check_sem(m->connect_sem))
+ {
+ int error;
+ socklen_t len = sizeof(error);
+
+ if ((m->rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
+ m->rc = error;
+ Log(TRACE_MIN, -1, "Posting connect semaphore for client %s rc %d", m->c->clientID, m->rc);
+ Thread_post_sem(m->connect_sem);
+ }
+#if defined(OPENSSL)
+ else if (m->c->connect_state == 2 && !Thread_check_sem(m->connect_sem))
+ {
+ rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket,
+ m->serverURI, m->c->sslopts->verify);
+ if (rc == 1 || rc == SSL_FATAL)
+ {
+ if (rc == 1 && !m->c->cleansession && m->c->session == NULL)
+ m->c->session = SSL_get1_session(m->c->net.ssl);
+ m->rc = rc;
+ Log(TRACE_MIN, -1, "Posting connect semaphore for SSL client %s rc %d", m->c->clientID, m->rc);
+ Thread_post_sem(m->connect_sem);
+ }
+ }
+#endif
+ }
+ }
+ run_id = 0;
+ running = tostop = 0;
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT;
+ return 0;
+}
+
+
+static void MQTTClient_stop(void)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (running == 1 && tostop == 0)
+ {
+ int conn_count = 0;
+ ListElement* current = NULL;
+
+ if (handles != NULL)
+ {
+ /* find out how many handles are still connected */
+ while (ListNextElement(handles, &current))
+ {
+ if (((MQTTClients*)(current->content))->c->connect_state > 0 ||
+ ((MQTTClients*)(current->content))->c->connected)
+ ++conn_count;
+ }
+ }
+ Log(TRACE_MIN, -1, "Conn_count is %d", conn_count);
+ /* stop the background thread, if we are the last one to be using it */
+ if (conn_count == 0)
+ {
+ int count = 0;
+ tostop = 1;
+ if (Thread_getid() != run_id)
+ {
+ while (running && ++count < 100)
+ {
+ Thread_unlock_mutex(mqttclient_mutex);
+ Log(TRACE_MIN, -1, "sleeping");
+ MQTTClient_sleep(100L);
+ Thread_lock_mutex(mqttclient_mutex);
+ }
+ }
+ rc = 1;
+ }
+ }
+ FUNC_EXIT_RC(rc);
+}
+
+
+int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl,
+ MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc)
+{
+ int rc = MQTTCLIENT_SUCCESS;
+ MQTTClients* m = handle;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (m == NULL || ma == NULL || m->c->connect_state != 0)
+ rc = MQTTCLIENT_FAILURE;
+ else
+ {
+ m->context = context;
+ m->cl = cl;
+ m->ma = ma;
+ m->dc = dc;
+ }
+
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void MQTTClient_closeSession(Clients* client)
+{
+ FUNC_ENTRY;
+ client->good = 0;
+ client->ping_outstanding = 0;
+ if (client->net.socket > 0)
+ {
+ if (client->connected)
+ MQTTPacket_send_disconnect(&client->net, client->clientID);
+ Thread_lock_mutex(socket_mutex);
+#if defined(OPENSSL)
+ SSLSocket_close(&client->net);
+#endif
+ Socket_close(client->net.socket);
+ Thread_unlock_mutex(socket_mutex);
+ client->net.socket = 0;
+#if defined(OPENSSL)
+ client->net.ssl = NULL;
+#endif
+ }
+ client->connected = 0;
+ client->connect_state = 0;
+
+ if (client->cleansession)
+ MQTTClient_cleanSession(client);
+ FUNC_EXIT;
+}
+
+
+static int MQTTClient_cleanSession(Clients* client)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+#if !defined(NO_PERSISTENCE)
+ rc = MQTTPersistence_clear(client);
+#endif
+ MQTTProtocol_emptyMessageList(client->inboundMsgs);
+ MQTTProtocol_emptyMessageList(client->outboundMsgs);
+ MQTTClient_emptyMessageQueue(client);
+ client->msgID = 0;
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+void Protocol_processPublication(Publish* publish, Clients* client)
+{
+ qEntry* qe = NULL;
+ MQTTClient_message* mm = NULL;
+
+ FUNC_ENTRY;
+ qe = malloc(sizeof(qEntry));
+ mm = malloc(sizeof(MQTTClient_message));
+
+ qe->msg = mm;
+
+ qe->topicName = publish->topic;
+ qe->topicLen = publish->topiclen;
+ publish->topic = NULL;
+
+ /* If the message is QoS 2, then we have already stored the incoming payload
+ * in an allocated buffer, so we don't need to copy again.
+ */
+ if (publish->header.bits.qos == 2)
+ mm->payload = publish->payload;
+ else
+ {
+ mm->payload = malloc(publish->payloadlen);
+ memcpy(mm->payload, publish->payload, publish->payloadlen);
+ }
+
+ mm->payloadlen = publish->payloadlen;
+ mm->qos = publish->header.bits.qos;
+ mm->retained = publish->header.bits.retain;
+ if (publish->header.bits.qos == 2)
+ mm->dup = 0; /* ensure that a QoS2 message is not passed to the application with dup = 1 */
+ else
+ mm->dup = publish->header.bits.dup;
+ mm->msgid = publish->msgId;
+
+ ListAppend(client->messageQueue, qe, sizeof(qe) + sizeof(mm) + mm->payloadlen + strlen(qe->topicName)+1);
+#if !defined(NO_PERSISTENCE)
+ if (client->persistence)
+ MQTTPersistence_persistQueueEntry(client, (MQTTPersistence_qEntry*)qe);
+#endif
+ FUNC_EXIT;
+}
+
+
+static int MQTTClient_connectURIVersion(MQTTClient handle, MQTTClient_connectOptions* options, const char* serverURI, int MQTTVersion,
+ START_TIME_TYPE start, long millisecsTimeout)
+{
+ MQTTClients* m = handle;
+ int rc = SOCKET_ERROR;
+ int sessionPresent = 0;
+
+ FUNC_ENTRY;
+ if (m->ma && !running)
+ {
+ Thread_start(MQTTClient_run, handle);
+ if (MQTTClient_elapsed(start) >= millisecsTimeout)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ MQTTClient_sleep(100L);
+ }
+
+ Log(TRACE_MIN, -1, "Connecting to serverURI %s with MQTT version %d", serverURI, MQTTVersion);
+#if defined(OPENSSL)
+ rc = MQTTProtocol_connect(serverURI, m->c, m->ssl, MQTTVersion);
+#else
+ rc = MQTTProtocol_connect(serverURI, m->c, MQTTVersion);
+#endif
+ if (rc == SOCKET_ERROR)
+ goto exit;
+
+ if (m->c->connect_state == 0)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+
+ if (m->c->connect_state == 1) /* TCP connect started - wait for completion */
+ {
+ Thread_unlock_mutex(mqttclient_mutex);
+ MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start));
+ Thread_lock_mutex(mqttclient_mutex);
+ if (rc != 0)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+
+#if defined(OPENSSL)
+ if (m->ssl)
+ {
+ int port;
+ char* hostname;
+ int setSocketForSSLrc = 0;
+
+ hostname = MQTTProtocol_addressPort(m->serverURI, &port);
+ setSocketForSSLrc = SSLSocket_setSocketForSSL(&m->c->net, m->c->sslopts, hostname);
+ if (hostname != m->serverURI)
+ free(hostname);
+
+ if (setSocketForSSLrc != MQTTCLIENT_SUCCESS)
+ {
+ if (m->c->session != NULL)
+ if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1)
+ Log(TRACE_MIN, -1, "Failed to set SSL session with stored data, non critical");
+ rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket,
+ m->serverURI, m->c->sslopts->verify);
+ if (rc == TCPSOCKET_INTERRUPTED)
+ m->c->connect_state = 2; /* the connect is still in progress */
+ else if (rc == SSL_FATAL)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ else if (rc == 1)
+ {
+ rc = MQTTCLIENT_SUCCESS;
+ m->c->connect_state = 3;
+ if (MQTTPacket_send_connect(m->c, MQTTVersion) == SOCKET_ERROR)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ if (!m->c->cleansession && m->c->session == NULL)
+ m->c->session = SSL_get1_session(m->c->net.ssl);
+ }
+ }
+ else
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ }
+ else
+ {
+#endif
+ m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */
+ if (MQTTPacket_send_connect(m->c, MQTTVersion) == SOCKET_ERROR)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+#if defined(OPENSSL)
+ }
+#endif
+ }
+
+#if defined(OPENSSL)
+ if (m->c->connect_state == 2) /* SSL connect sent - wait for completion */
+ {
+ Thread_unlock_mutex(mqttclient_mutex);
+ MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start));
+ Thread_lock_mutex(mqttclient_mutex);
+ if (rc != 1)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ if(!m->c->cleansession && m->c->session == NULL)
+ m->c->session = SSL_get1_session(m->c->net.ssl);
+ m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */
+ if (MQTTPacket_send_connect(m->c, MQTTVersion) == SOCKET_ERROR)
+ {
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ }
+#endif
+
+ if (m->c->connect_state == 3) /* MQTT connect sent - wait for CONNACK */
+ {
+ MQTTPacket* pack = NULL;
+
+ Thread_unlock_mutex(mqttclient_mutex);
+ pack = MQTTClient_waitfor(handle, CONNACK, &rc, millisecsTimeout - MQTTClient_elapsed(start));
+ Thread_lock_mutex(mqttclient_mutex);
+ if (pack == NULL)
+ rc = SOCKET_ERROR;
+ else
+ {
+ Connack* connack = (Connack*)pack;
+ Log(TRACE_PROTOCOL, 1, NULL, m->c->net.socket, m->c->clientID, connack->rc);
+ if ((rc = connack->rc) == MQTTCLIENT_SUCCESS)
+ {
+ m->c->connected = 1;
+ m->c->good = 1;
+ m->c->connect_state = 0;
+ if (MQTTVersion == 4)
+ sessionPresent = connack->flags.bits.sessionPresent;
+ if (m->c->cleansession)
+ rc = MQTTClient_cleanSession(m->c);
+ if (m->c->outboundMsgs->count > 0)
+ {
+ ListElement* outcurrent = NULL;
+
+ while (ListNextElement(m->c->outboundMsgs, &outcurrent))
+ {
+ Messages* m = (Messages*)(outcurrent->content);
+ m->lastTouch = 0;
+ }
+ MQTTProtocol_retry((time_t)0, 1, 1);
+ if (m->c->connected != 1)
+ rc = MQTTCLIENT_DISCONNECTED;
+ }
+ }
+ free(connack);
+ m->pack = NULL;
+ }
+ }
+exit:
+ if (rc == MQTTCLIENT_SUCCESS)
+ {
+ if (options->struct_version == 4) /* means we have to fill out return values */
+ {
+ options->returned.serverURI = serverURI;
+ options->returned.MQTTVersion = MQTTVersion;
+ options->returned.sessionPresent = sessionPresent;
+ }
+ }
+ else
+ MQTTClient_disconnect1(handle, 0, 0, (MQTTVersion == 3)); /* don't want to call connection lost */
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+static int retryLoopInterval = 5;
+
+static void setRetryLoopInterval(int keepalive)
+{
+ int proposed = keepalive / 10;
+
+ if (proposed < 1)
+ proposed = 1;
+ else if (proposed > 5)
+ proposed = 5;
+ if (proposed < retryLoopInterval)
+ retryLoopInterval = proposed;
+}
+
+
+static int MQTTClient_connectURI(MQTTClient handle, MQTTClient_connectOptions* options, const char* serverURI)
+{
+ MQTTClients* m = handle;
+ START_TIME_TYPE start;
+ long millisecsTimeout = 30000L;
+ int rc = SOCKET_ERROR;
+ int MQTTVersion = 0;
+
+ FUNC_ENTRY;
+ millisecsTimeout = options->connectTimeout * 1000;
+ start = MQTTClient_start_clock();
+
+ m->c->keepAliveInterval = options->keepAliveInterval;
+ setRetryLoopInterval(options->keepAliveInterval);
+ m->c->cleansession = options->cleansession;
+ m->c->maxInflightMessages = (options->reliable) ? 1 : 10;
+
+ if (m->c->will)
+ {
+ free(m->c->will->payload);
+ free(m->c->will->topic);
+ free(m->c->will);
+ m->c->will = NULL;
+ }
+
+ if (options->will && (options->will->struct_version == 0 || options->will->struct_version == 1))
+ {
+ const void* source = NULL;
+
+ m->c->will = malloc(sizeof(willMessages));
+ if (options->will->message || (options->will->struct_version == 1 && options->will->payload.data))
+ {
+ if (options->will->struct_version == 1 && options->will->payload.data)
+ {
+ m->c->will->payloadlen = options->will->payload.len;
+ source = options->will->payload.data;
+ }
+ else
+ {
+ m->c->will->payloadlen = (int)strlen(options->will->message);
+ source = (void*)options->will->message;
+ }
+ m->c->will->payload = malloc(m->c->will->payloadlen);
+ memcpy(m->c->will->payload, source, m->c->will->payloadlen);
+ }
+ else
+ {
+ m->c->will->payload = NULL;
+ m->c->will->payloadlen = 0;
+ }
+ m->c->will->qos = options->will->qos;
+ m->c->will->retained = options->will->retained;
+ m->c->will->topic = MQTTStrdup(options->will->topicName);
+ }
+
+#if defined(OPENSSL)
+ if (m->c->sslopts)
+ {
+ if (m->c->sslopts->trustStore)
+ free((void*)m->c->sslopts->trustStore);
+ if (m->c->sslopts->keyStore)
+ free((void*)m->c->sslopts->keyStore);
+ if (m->c->sslopts->privateKey)
+ free((void*)m->c->sslopts->privateKey);
+ if (m->c->sslopts->privateKeyPassword)
+ free((void*)m->c->sslopts->privateKeyPassword);
+ if (m->c->sslopts->enabledCipherSuites)
+ free((void*)m->c->sslopts->enabledCipherSuites);
+ if (m->c->sslopts->struct_version >= 2)
+ {
+ if (m->c->sslopts->CApath)
+ free((void*)m->c->sslopts->CApath);
+ }
+ free(m->c->sslopts);
+ m->c->sslopts = NULL;
+ }
+
+ if (options->struct_version != 0 && options->ssl)
+ {
+ m->c->sslopts = malloc(sizeof(MQTTClient_SSLOptions));
+ memset(m->c->sslopts, '\0', sizeof(MQTTClient_SSLOptions));
+ m->c->sslopts->struct_version = options->ssl->struct_version;
+ if (options->ssl->trustStore)
+ m->c->sslopts->trustStore = MQTTStrdup(options->ssl->trustStore);
+ if (options->ssl->keyStore)
+ m->c->sslopts->keyStore = MQTTStrdup(options->ssl->keyStore);
+ if (options->ssl->privateKey)
+ m->c->sslopts->privateKey = MQTTStrdup(options->ssl->privateKey);
+ if (options->ssl->privateKeyPassword)
+ m->c->sslopts->privateKeyPassword = MQTTStrdup(options->ssl->privateKeyPassword);
+ if (options->ssl->enabledCipherSuites)
+ m->c->sslopts->enabledCipherSuites = MQTTStrdup(options->ssl->enabledCipherSuites);
+ m->c->sslopts->enableServerCertAuth = options->ssl->enableServerCertAuth;
+ if (m->c->sslopts->struct_version >= 1)
+ m->c->sslopts->sslVersion = options->ssl->sslVersion;
+ if (m->c->sslopts->struct_version >= 2)
+ {
+ m->c->sslopts->verify = options->ssl->verify;
+ if (m->c->sslopts->CApath)
+ m->c->sslopts->CApath = MQTTStrdup(options->ssl->CApath);
+ }
+ }
+#endif
+
+ if (m->c->username)
+ free((void*)m->c->username);
+ if (options->username)
+ m->c->username = MQTTStrdup(options->username);
+ if (m->c->password)
+ free((void*)m->c->password);
+ if (options->password)
+ {
+ m->c->password = MQTTStrdup(options->password);
+ m->c->passwordlen = (int)strlen(options->password);
+ }
+ else if (options->struct_version >= 5 && options->binarypwd.data)
+ {
+ m->c->passwordlen = options->binarypwd.len;
+ m->c->password = malloc(m->c->passwordlen);
+ memcpy((void*)m->c->password, options->binarypwd.data, m->c->passwordlen);
+ }
+
+ if (options->struct_version >= 3)
+ MQTTVersion = options->MQTTVersion;
+ else
+ MQTTVersion = MQTTVERSION_DEFAULT;
+
+ if (MQTTVersion == MQTTVERSION_DEFAULT)
+ {
+ if ((rc = MQTTClient_connectURIVersion(handle, options, serverURI, 4, start, millisecsTimeout)) != MQTTCLIENT_SUCCESS)
+ rc = MQTTClient_connectURIVersion(handle, options, serverURI, 3, start, millisecsTimeout);
+ }
+ else
+ rc = MQTTClient_connectURIVersion(handle, options, serverURI, MQTTVersion, start, millisecsTimeout);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
+{
+ MQTTClients* m = handle;
+ int rc = SOCKET_ERROR;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(connect_mutex);
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (options == NULL)
+ {
+ rc = MQTTCLIENT_NULL_PARAMETER;
+ goto exit;
+ }
+
+ if (strncmp(options->struct_id, "MQTC", 4) != 0 || options->struct_version < 0 || options->struct_version > 5)
+ {
+ rc = MQTTCLIENT_BAD_STRUCTURE;
+ goto exit;
+ }
+
+#if defined(OPENSSL)
+ if (m->ssl && options->ssl == NULL)
+ {
+ rc = MQTTCLIENT_NULL_PARAMETER;
+ goto exit;
+ }
+#endif
+
+ if (options->will) /* check validity of will options structure */
+ {
+ if (strncmp(options->will->struct_id, "MQTW", 4) != 0 || (options->will->struct_version != 0 && options->will->struct_version != 1))
+ {
+ rc = MQTTCLIENT_BAD_STRUCTURE;
+ goto exit;
+ }
+ }
+
+
+#if defined(OPENSSL)
+ if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */
+ {
+ if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 2)
+ {
+ rc = MQTTCLIENT_BAD_STRUCTURE;
+ goto exit;
+ }
+ }
+#endif
+
+ if ((options->username && !UTF8_validateString(options->username)) ||
+ (options->password && !UTF8_validateString(options->password)))
+ {
+ rc = MQTTCLIENT_BAD_UTF8_STRING;
+ goto exit;
+ }
+
+ if (options->struct_version < 2 || options->serverURIcount == 0)
+ rc = MQTTClient_connectURI(handle, options, m->serverURI);
+ else
+ {
+ int i;
+
+ for (i = 0; i < options->serverURIcount; ++i)
+ {
+ char* serverURI = options->serverURIs[i];
+
+ if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
+ serverURI += strlen(URI_TCP);
+#if defined(OPENSSL)
+ else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
+ {
+ serverURI += strlen(URI_SSL);
+ m->ssl = 1;
+ }
+#endif
+ if ((rc = MQTTClient_connectURI(handle, options, serverURI)) == MQTTCLIENT_SUCCESS)
+ break;
+ }
+ }
+
+exit:
+ if (m->c->will)
+ {
+ if (m->c->will->payload)
+ free(m->c->will->payload);
+ if (m->c->will->topic)
+ free(m->c->will->topic);
+ free(m->c->will);
+ m->c->will = NULL;
+ }
+ Thread_unlock_mutex(mqttclient_mutex);
+ Thread_unlock_mutex(connect_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * mqttclient_mutex must be locked when you call this function, if multi threaded
+ */
+static int MQTTClient_disconnect1(MQTTClient handle, int timeout, int call_connection_lost, int stop)
+{
+ MQTTClients* m = handle;
+ START_TIME_TYPE start;
+ int rc = MQTTCLIENT_SUCCESS;
+ int was_connected = 0;
+
+ FUNC_ENTRY;
+ if (m == NULL || m->c == NULL)
+ {
+ rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+ if (m->c->connected == 0 && m->c->connect_state == 0)
+ {
+ rc = MQTTCLIENT_DISCONNECTED;
+ goto exit;
+ }
+ was_connected = m->c->connected; /* should be 1 */
+ if (m->c->connected != 0)
+ {
+ start = MQTTClient_start_clock();
+ m->c->connect_state = -2; /* indicate disconnecting */
+ while (m->c->inboundMsgs->count > 0 || m->c->outboundMsgs->count > 0)
+ { /* wait for all inflight message flows to finish, up to timeout */
+ if (MQTTClient_elapsed(start) >= timeout)
+ break;
+ Thread_unlock_mutex(mqttclient_mutex);
+ MQTTClient_yield();
+ Thread_lock_mutex(mqttclient_mutex);
+ }
+ }
+
+ MQTTClient_closeSession(m->c);
+
+ while (Thread_check_sem(m->connect_sem))
+ Thread_wait_sem(m->connect_sem, 100);
+ while (Thread_check_sem(m->connack_sem))
+ Thread_wait_sem(m->connack_sem, 100);
+ while (Thread_check_sem(m->suback_sem))
+ Thread_wait_sem(m->suback_sem, 100);
+ while (Thread_check_sem(m->unsuback_sem))
+ Thread_wait_sem(m->unsuback_sem, 100);
+exit:
+ if (stop)
+ MQTTClient_stop();
+ if (call_connection_lost && m->cl && was_connected)
+ {
+ Log(TRACE_MIN, -1, "Calling connectionLost for client %s", m->c->clientID);
+ Thread_start(connectionLost_call, m);
+ }
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * mqttclient_mutex must be locked when you call this function, if multi threaded
+ */
+static int MQTTClient_disconnect_internal(MQTTClient handle, int timeout)
+{
+ return MQTTClient_disconnect1(handle, timeout, 1, 1);
+}
+
+
+/**
+ * mqttclient_mutex must be locked when you call this function, if multi threaded
+ */
+void MQTTProtocol_closeSession(Clients* c, int sendwill)
+{
+ MQTTClient_disconnect_internal((MQTTClient)c->context, 0);
+}
+
+
+int MQTTClient_disconnect(MQTTClient handle, int timeout)
+{
+ int rc = 0;
+
+ Thread_lock_mutex(mqttclient_mutex);
+ rc = MQTTClient_disconnect1(handle, timeout, 0, 1);
+ Thread_unlock_mutex(mqttclient_mutex);
+ return rc;
+}
+
+
+int MQTTClient_isConnected(MQTTClient handle)
+{
+ MQTTClients* m = handle;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(mqttclient_mutex);
+ if (m && m->c)
+ rc = m->c->connected;
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTClient_subscribeMany(MQTTClient handle, int count, char* const* topic, int* qos)
+{
+ MQTTClients* m = handle;
+ List* topics = NULL;
+ List* qoss = NULL;
+ int i = 0;
+ int rc = MQTTCLIENT_FAILURE;
+ int msgid = 0;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(subscribe_mutex);
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (m == NULL || m->c == NULL)
+ {
+ rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+ if (m->c->connected == 0)
+ {
+ rc = MQTTCLIENT_DISCONNECTED;
+ goto exit;
+ }
+ for (i = 0; i < count; i++)
+ {
+ if (!UTF8_validateString(topic[i]))
+ {
+ rc = MQTTCLIENT_BAD_UTF8_STRING;
+ goto exit;
+ }
+
+ if(qos[i] < 0 || qos[i] > 2)
+ {
+ rc = MQTTCLIENT_BAD_QOS;
+ goto exit;
+ }
+ }
+ if ((msgid = MQTTProtocol_assignMsgId(m->c)) == 0)
+ {
+ rc = MQTTCLIENT_MAX_MESSAGES_INFLIGHT;
+ goto exit;
+ }
+
+ topics = ListInitialize();
+ qoss = ListInitialize();
+ for (i = 0; i < count; i++)
+ {
+ ListAppend(topics, topic[i], strlen(topic[i]));
+ ListAppend(qoss, &qos[i], sizeof(int));
+ }
+
+ rc = MQTTProtocol_subscribe(m->c, topics, qoss, msgid);
+ ListFreeNoContent(topics);
+ ListFreeNoContent(qoss);
+
+ if (rc == TCPSOCKET_COMPLETE)
+ {
+ MQTTPacket* pack = NULL;
+
+ Thread_unlock_mutex(mqttclient_mutex);
+ pack = MQTTClient_waitfor(handle, SUBACK, &rc, 10000L);
+ Thread_lock_mutex(mqttclient_mutex);
+ if (pack != NULL)
+ {
+ Suback* sub = (Suback*)pack;
+ ListElement* current = NULL;
+ i = 0;
+ while (ListNextElement(sub->qoss, &current))
+ {
+ int* reqqos = (int*)(current->content);
+ qos[i++] = *reqqos;
+ }
+ rc = MQTTProtocol_handleSubacks(pack, m->c->net.socket);
+ m->pack = NULL;
+ }
+ else
+ rc = SOCKET_ERROR;
+ }
+
+ if (rc == SOCKET_ERROR)
+ MQTTClient_disconnect_internal(handle, 0);
+ else if (rc == TCPSOCKET_COMPLETE)
+ rc = MQTTCLIENT_SUCCESS;
+
+exit:
+ Thread_unlock_mutex(mqttclient_mutex);
+ Thread_unlock_mutex(subscribe_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTClient_subscribe(MQTTClient handle, const char* topic, int qos)
+{
+ int rc = 0;
+ char *const topics[] = {(char*)topic};
+
+ FUNC_ENTRY;
+ rc = MQTTClient_subscribeMany(handle, 1, topics, &qos);
+ if (qos == MQTT_BAD_SUBSCRIBE) /* addition for MQTT 3.1.1 - error code from subscribe */
+ rc = MQTT_BAD_SUBSCRIBE;
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char* const* topic)
+{
+ MQTTClients* m = handle;
+ List* topics = NULL;
+ int i = 0;
+ int rc = SOCKET_ERROR;
+ int msgid = 0;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(unsubscribe_mutex);
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (m == NULL || m->c == NULL)
+ {
+ rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+ if (m->c->connected == 0)
+ {
+ rc = MQTTCLIENT_DISCONNECTED;
+ goto exit;
+ }
+ for (i = 0; i < count; i++)
+ {
+ if (!UTF8_validateString(topic[i]))
+ {
+ rc = MQTTCLIENT_BAD_UTF8_STRING;
+ goto exit;
+ }
+ }
+ if ((msgid = MQTTProtocol_assignMsgId(m->c)) == 0)
+ {
+ rc = MQTTCLIENT_MAX_MESSAGES_INFLIGHT;
+ goto exit;
+ }
+
+ topics = ListInitialize();
+ for (i = 0; i < count; i++)
+ ListAppend(topics, topic[i], strlen(topic[i]));
+ rc = MQTTProtocol_unsubscribe(m->c, topics, msgid);
+ ListFreeNoContent(topics);
+
+ if (rc == TCPSOCKET_COMPLETE)
+ {
+ MQTTPacket* pack = NULL;
+
+ Thread_unlock_mutex(mqttclient_mutex);
+ pack = MQTTClient_waitfor(handle, UNSUBACK, &rc, 10000L);
+ Thread_lock_mutex(mqttclient_mutex);
+ if (pack != NULL)
+ {
+ rc = MQTTProtocol_handleUnsubacks(pack, m->c->net.socket);
+ m->pack = NULL;
+ }
+ else
+ rc = SOCKET_ERROR;
+ }
+
+ if (rc == SOCKET_ERROR)
+ MQTTClient_disconnect_internal(handle, 0);
+
+exit:
+ Thread_unlock_mutex(mqttclient_mutex);
+ Thread_unlock_mutex(unsubscribe_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTClient_unsubscribe(MQTTClient handle, const char* topic)
+{
+ int rc = 0;
+ char *const topics[] = {(char*)topic};
+ FUNC_ENTRY;
+ rc = MQTTClient_unsubscribeMany(handle, 1, topics);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTClient_publish(MQTTClient handle, const char* topicName, int payloadlen, void* payload,
+ int qos, int retained, MQTTClient_deliveryToken* deliveryToken)
+{
+ int rc = MQTTCLIENT_SUCCESS;
+ MQTTClients* m = handle;
+ Messages* msg = NULL;
+ Publish* p = NULL;
+ int blocked = 0;
+ int msgid = 0;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (m == NULL || m->c == NULL)
+ rc = MQTTCLIENT_FAILURE;
+ else if (m->c->connected == 0)
+ rc = MQTTCLIENT_DISCONNECTED;
+ else if (!UTF8_validateString(topicName))
+ rc = MQTTCLIENT_BAD_UTF8_STRING;
+ if (rc != MQTTCLIENT_SUCCESS)
+ goto exit;
+
+ /* If outbound queue is full, block until it is not */
+ while (m->c->outboundMsgs->count >= m->c->maxInflightMessages ||
+ Socket_noPendingWrites(m->c->net.socket) == 0) /* wait until the socket is free of large packets being written */
+ {
+ if (blocked == 0)
+ {
+ blocked = 1;
+ Log(TRACE_MIN, -1, "Blocking publish on queue full for client %s", m->c->clientID);
+ }
+ Thread_unlock_mutex(mqttclient_mutex);
+ MQTTClient_yield();
+ Thread_lock_mutex(mqttclient_mutex);
+ if (m->c->connected == 0)
+ {
+ rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+ }
+ if (blocked == 1)
+ Log(TRACE_MIN, -1, "Resuming publish now queue not full for client %s", m->c->clientID);
+ if (qos > 0 && (msgid = MQTTProtocol_assignMsgId(m->c)) == 0)
+ { /* this should never happen as we've waited for spaces in the queue */
+ rc = MQTTCLIENT_MAX_MESSAGES_INFLIGHT;
+ goto exit;
+ }
+
+ p = malloc(sizeof(Publish));
+
+ p->payload = payload;
+ p->payloadlen = payloadlen;
+ p->topic = (char*)topicName;
+ p->msgId = msgid;
+
+ rc = MQTTProtocol_startPublish(m->c, p, qos, retained, &msg);
+
+ /* If the packet was partially written to the socket, wait for it to complete.
+ * However, if the client is disconnected during this time and qos is not 0, still return success, as
+ * the packet has already been written to persistence and assigned a message id so will
+ * be sent when the client next connects.
+ */
+ if (rc == TCPSOCKET_INTERRUPTED)
+ {
+ while (m->c->connected == 1 && SocketBuffer_getWrite(m->c->net.socket))
+ {
+ Thread_unlock_mutex(mqttclient_mutex);
+ MQTTClient_yield();
+ Thread_lock_mutex(mqttclient_mutex);
+ }
+ rc = (qos > 0 || m->c->connected == 1) ? MQTTCLIENT_SUCCESS : MQTTCLIENT_FAILURE;
+ }
+
+ if (deliveryToken && qos > 0)
+ *deliveryToken = msg->msgid;
+
+ free(p);
+
+ if (rc == SOCKET_ERROR)
+ {
+ MQTTClient_disconnect_internal(handle, 0);
+ /* Return success for qos > 0 as the send will be retried automatically */
+ rc = (qos > 0) ? MQTTCLIENT_SUCCESS : MQTTCLIENT_FAILURE;
+ }
+
+exit:
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* message,
+ MQTTClient_deliveryToken* deliveryToken)
+{
+ int rc = MQTTCLIENT_SUCCESS;
+
+ FUNC_ENTRY;
+ if (message == NULL)
+ {
+ rc = MQTTCLIENT_NULL_PARAMETER;
+ goto exit;
+ }
+
+ if (strncmp(message->struct_id, "MQTM", 4) != 0 || message->struct_version != 0)
+ {
+ rc = MQTTCLIENT_BAD_STRUCTURE;
+ goto exit;
+ }
+
+ rc = MQTTClient_publish(handle, topicName, message->payloadlen, message->payload,
+ message->qos, message->retained, deliveryToken);
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static void MQTTClient_retry(void)
+{
+ static time_t last = 0L;
+ time_t now;
+
+ FUNC_ENTRY;
+ time(&(now));
+ if (difftime(now, last) > retryLoopInterval)
+ {
+ time(&(last));
+ MQTTProtocol_keepalive(now);
+ MQTTProtocol_retry(now, 1, 0);
+ }
+ else
+ MQTTProtocol_retry(now, 0, 0);
+ FUNC_EXIT;
+}
+
+
+static MQTTPacket* MQTTClient_cycle(int* sock, unsigned long timeout, int* rc)
+{
+ struct timeval tp = {0L, 0L};
+ static Ack ack;
+ MQTTPacket* pack = NULL;
+
+ FUNC_ENTRY;
+ if (timeout > 0L)
+ {
+ tp.tv_sec = timeout / 1000;
+ tp.tv_usec = (timeout % 1000) * 1000; /* this field is microseconds! */
+ }
+
+#if defined(OPENSSL)
+ if ((*sock = SSLSocket_getPendingRead()) == -1)
+ {
+ /* 0 from getReadySocket indicates no work to do, -1 == error, but can happen normally */
+#endif
+ *sock = Socket_getReadySocket(0, &tp, socket_mutex);
+#if defined(OPENSSL)
+ }
+#endif
+ Thread_lock_mutex(mqttclient_mutex);
+ if (*sock > 0)
+ {
+ MQTTClients* m = NULL;
+ if (ListFindItem(handles, sock, clientSockCompare) != NULL)
+ m = (MQTTClient)(handles->current->content);
+ if (m != NULL)
+ {
+ if (m->c->connect_state == 1 || m->c->connect_state == 2)
+ *rc = 0; /* waiting for connect state to clear */
+ else
+ {
+ pack = MQTTPacket_Factory(&m->c->net, rc);
+ if (*rc == TCPSOCKET_INTERRUPTED)
+ *rc = 0;
+ }
+ }
+ if (pack)
+ {
+ int freed = 1;
+
+ /* Note that these handle... functions free the packet structure that they are dealing with */
+ if (pack->header.bits.type == PUBLISH)
+ *rc = MQTTProtocol_handlePublishes(pack, *sock);
+ else if (pack->header.bits.type == PUBACK || pack->header.bits.type == PUBCOMP)
+ {
+ int msgid;
+
+ ack = (pack->header.bits.type == PUBCOMP) ? *(Pubcomp*)pack : *(Puback*)pack;
+ msgid = ack.msgId;
+ *rc = (pack->header.bits.type == PUBCOMP) ?
+ MQTTProtocol_handlePubcomps(pack, *sock) : MQTTProtocol_handlePubacks(pack, *sock);
+ if (m && m->dc)
+ {
+ Log(TRACE_MIN, -1, "Calling deliveryComplete for client %s, msgid %d", m->c->clientID, msgid);
+ (*(m->dc))(m->context, msgid);
+ }
+ }
+ else if (pack->header.bits.type == PUBREC)
+ *rc = MQTTProtocol_handlePubrecs(pack, *sock);
+ else if (pack->header.bits.type == PUBREL)
+ *rc = MQTTProtocol_handlePubrels(pack, *sock);
+ else if (pack->header.bits.type == PINGRESP)
+ *rc = MQTTProtocol_handlePingresps(pack, *sock);
+ else
+ freed = 0;
+ if (freed)
+ pack = NULL;
+ }
+ }
+ MQTTClient_retry();
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT_RC(*rc);
+ return pack;
+}
+
+
+static MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, long timeout)
+{
+ MQTTPacket* pack = NULL;
+ MQTTClients* m = handle;
+ START_TIME_TYPE start = MQTTClient_start_clock();
+
+ FUNC_ENTRY;
+ if (((MQTTClients*)handle) == NULL || timeout <= 0L)
+ {
+ *rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+
+ if (running)
+ {
+ if (packet_type == CONNECT)
+ {
+ if ((*rc = Thread_wait_sem(m->connect_sem, timeout)) == 0)
+ *rc = m->rc;
+ }
+ else if (packet_type == CONNACK)
+ *rc = Thread_wait_sem(m->connack_sem, timeout);
+ else if (packet_type == SUBACK)
+ *rc = Thread_wait_sem(m->suback_sem, timeout);
+ else if (packet_type == UNSUBACK)
+ *rc = Thread_wait_sem(m->unsuback_sem, timeout);
+ if (*rc == 0 && packet_type != CONNECT && m->pack == NULL)
+ Log(LOG_ERROR, -1, "waitfor unexpectedly is NULL for client %s, packet_type %d, timeout %ld", m->c->clientID, packet_type, timeout);
+ pack = m->pack;
+ }
+ else
+ {
+ *rc = TCPSOCKET_COMPLETE;
+ while (1)
+ {
+ int sock = -1;
+ pack = MQTTClient_cycle(&sock, 100L, rc);
+ if (sock == m->c->net.socket)
+ {
+ if (*rc == SOCKET_ERROR)
+ break;
+ if (pack && (pack->header.bits.type == packet_type))
+ break;
+ if (m->c->connect_state == 1)
+ {
+ int error;
+ socklen_t len = sizeof(error);
+
+ if ((*rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
+ *rc = error;
+ break;
+ }
+#if defined(OPENSSL)
+ else if (m->c->connect_state == 2)
+ {
+ *rc = SSLSocket_connect(m->c->net.ssl, sock,
+ m->serverURI, m->c->sslopts->verify);
+ if (*rc == SSL_FATAL)
+ break;
+ else if (*rc == 1) /* rc == 1 means SSL connect has finished and succeeded */
+ {
+ if (!m->c->cleansession && m->c->session == NULL)
+ m->c->session = SSL_get1_session(m->c->net.ssl);
+ break;
+ }
+ }
+#endif
+ else if (m->c->connect_state == 3)
+ {
+ int error;
+ socklen_t len = sizeof(error);
+
+ if (getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len) == 0)
+ {
+ if (error)
+ {
+ *rc = error;
+ break;
+ }
+ }
+ }
+ }
+ if (MQTTClient_elapsed(start) > timeout)
+ {
+ pack = NULL;
+ break;
+ }
+ }
+ }
+
+exit:
+ FUNC_EXIT_RC(*rc);
+ return pack;
+}
+
+
+int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTClient_message** message,
+ unsigned long timeout)
+{
+ int rc = TCPSOCKET_COMPLETE;
+ START_TIME_TYPE start = MQTTClient_start_clock();
+ unsigned long elapsed = 0L;
+ MQTTClients* m = handle;
+
+ FUNC_ENTRY;
+ if (m == NULL || m->c == NULL
+ || running) /* receive is not meant to be called in a multi-thread environment */
+ {
+ rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+ if (m->c->connected == 0)
+ {
+ rc = MQTTCLIENT_DISCONNECTED;
+ goto exit;
+ }
+
+ *topicName = NULL;
+ *message = NULL;
+
+ /* if there is already a message waiting, don't hang around but still do some packet handling */
+ if (m->c->messageQueue->count > 0)
+ timeout = 0L;
+
+ elapsed = MQTTClient_elapsed(start);
+ do
+ {
+ int sock = 0;
+ MQTTClient_cycle(&sock, (timeout > elapsed) ? timeout - elapsed : 0L, &rc);
+
+ if (rc == SOCKET_ERROR)
+ {
+ if (ListFindItem(handles, &sock, clientSockCompare) && /* find client corresponding to socket */
+ (MQTTClient)(handles->current->content) == handle)
+ break; /* there was an error on the socket we are interested in */
+ }
+ elapsed = MQTTClient_elapsed(start);
+ }
+ while (elapsed < timeout && m->c->messageQueue->count == 0);
+
+ if (m->c->messageQueue->count > 0)
+ rc = MQTTClient_deliverMessage(rc, m, topicName, topicLen, message);
+
+ if (rc == SOCKET_ERROR)
+ MQTTClient_disconnect_internal(handle, 0);
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+void MQTTClient_yield(void)
+{
+ START_TIME_TYPE start = MQTTClient_start_clock();
+ unsigned long elapsed = 0L;
+ unsigned long timeout = 100L;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (running) /* yield is not meant to be called in a multi-thread environment */
+ {
+ MQTTClient_sleep(timeout);
+ goto exit;
+ }
+
+ elapsed = MQTTClient_elapsed(start);
+ do
+ {
+ int sock = -1;
+ MQTTClient_cycle(&sock, (timeout > elapsed) ? timeout - elapsed : 0L, &rc);
+ Thread_lock_mutex(mqttclient_mutex);
+ if (rc == SOCKET_ERROR && ListFindItem(handles, &sock, clientSockCompare))
+ {
+ MQTTClients* m = (MQTTClient)(handles->current->content);
+ if (m->c->connect_state != -2)
+ MQTTClient_disconnect_internal(m, 0);
+ }
+ Thread_unlock_mutex(mqttclient_mutex);
+ elapsed = MQTTClient_elapsed(start);
+ }
+ while (elapsed < timeout);
+exit:
+ FUNC_EXIT;
+}
+
+/*
+static int pubCompare(void* a, void* b)
+{
+ Messages* msg = (Messages*)a;
+ return msg->publish == (Publications*)b;
+}*/
+
+
+int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken mdt, unsigned long timeout)
+{
+ int rc = MQTTCLIENT_FAILURE;
+ START_TIME_TYPE start = MQTTClient_start_clock();
+ unsigned long elapsed = 0L;
+ MQTTClients* m = handle;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (m == NULL || m->c == NULL)
+ {
+ rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+
+ elapsed = MQTTClient_elapsed(start);
+ while (elapsed < timeout)
+ {
+ if (m->c->connected == 0)
+ {
+ rc = MQTTCLIENT_DISCONNECTED;
+ goto exit;
+ }
+ if (ListFindItem(m->c->outboundMsgs, &mdt, messageIDCompare) == NULL)
+ {
+ rc = MQTTCLIENT_SUCCESS; /* well we couldn't find it */
+ goto exit;
+ }
+ Thread_unlock_mutex(mqttclient_mutex);
+ MQTTClient_yield();
+ Thread_lock_mutex(mqttclient_mutex);
+ elapsed = MQTTClient_elapsed(start);
+ }
+
+exit:
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTClient_getPendingDeliveryTokens(MQTTClient handle, MQTTClient_deliveryToken **tokens)
+{
+ int rc = MQTTCLIENT_SUCCESS;
+ MQTTClients* m = handle;
+ *tokens = NULL;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(mqttclient_mutex);
+
+ if (m == NULL)
+ {
+ rc = MQTTCLIENT_FAILURE;
+ goto exit;
+ }
+
+ if (m->c && m->c->outboundMsgs->count > 0)
+ {
+ ListElement* current = NULL;
+ int count = 0;
+
+ *tokens = malloc(sizeof(MQTTClient_deliveryToken) * (m->c->outboundMsgs->count + 1));
+ /*Heap_unlink(__FILE__, __LINE__, *tokens);*/
+ while (ListNextElement(m->c->outboundMsgs, &current))
+ {
+ Messages* m = (Messages*)(current->content);
+ (*tokens)[count++] = m->msgid;
+ }
+ (*tokens)[count] = -1;
+ }
+
+exit:
+ Thread_unlock_mutex(mqttclient_mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+MQTTClient_nameValue* MQTTClient_getVersionInfo(void)
+{
+ #define MAX_INFO_STRINGS 8
+ static MQTTClient_nameValue libinfo[MAX_INFO_STRINGS + 1];
+ int i = 0;
+
+ libinfo[i].name = "Product name";
+ libinfo[i++].value = "Paho Synchronous MQTT C Client Library";
+
+ libinfo[i].name = "Version";
+ libinfo[i++].value = CLIENT_VERSION;
+
+ libinfo[i].name = "Build level";
+ libinfo[i++].value = BUILD_TIMESTAMP;
+#if defined(OPENSSL)
+ libinfo[i].name = "OpenSSL version";
+ libinfo[i++].value = SSLeay_version(SSLEAY_VERSION);
+
+ libinfo[i].name = "OpenSSL flags";
+ libinfo[i++].value = SSLeay_version(SSLEAY_CFLAGS);
+
+ libinfo[i].name = "OpenSSL build timestamp";
+ libinfo[i++].value = SSLeay_version(SSLEAY_BUILT_ON);
+
+ libinfo[i].name = "OpenSSL platform";
+ libinfo[i++].value = SSLeay_version(SSLEAY_PLATFORM);
+
+ libinfo[i].name = "OpenSSL directory";
+ libinfo[i++].value = SSLeay_version(SSLEAY_DIR);
+#endif
+ libinfo[i].name = NULL;
+ libinfo[i].value = NULL;
+ return libinfo;
+}
+
+
+/**
+ * See if any pending writes have been completed, and cleanup if so.
+ * Cleaning up means removing any publication data that was stored because the write did
+ * not originally complete.
+ */
+static void MQTTProtocol_checkPendingWrites(void)
+{
+ FUNC_ENTRY;
+ if (state.pending_writes.count > 0)
+ {
+ ListElement* le = state.pending_writes.first;
+ while (le)
+ {
+ if (Socket_noPendingWrites(((pending_write*)(le->content))->socket))
+ {
+ MQTTProtocol_removePublication(((pending_write*)(le->content))->p);
+ state.pending_writes.current = le;
+ ListRemove(&(state.pending_writes), le->content); /* does NextElement itself */
+ le = state.pending_writes.current;
+ }
+ else
+ ListNextElement(&(state.pending_writes), &le);
+ }
+ }
+ FUNC_EXIT;
+}
+
+
+static void MQTTClient_writeComplete(int socket, int rc)
+{
+ ListElement* found = NULL;
+
+ FUNC_ENTRY;
+ /* a partial write is now complete for a socket - this will be on a publish*/
+
+ MQTTProtocol_checkPendingWrites();
+
+ /* find the client using this socket */
+ if ((found = ListFindItem(handles, &socket, clientSockCompare)) != NULL)
+ {
+ MQTTClients* m = (MQTTClients*)(found->content);
+
+ time(&(m->c->net.lastSent));
+ }
+ FUNC_EXIT;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTClient.h b/tts-mqtt-test/src/paho-mqtt/MQTTClient.h
new file mode 100644
index 0000000..346cbd4
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTClient.h
@@ -0,0 +1,1414 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - multiple server connection support
+ * Ian Craggs - MQTT 3.1.1 support
+ * Ian Craggs - remove const from eyecatchers #168
+ *******************************************************************************/
+
+/**
+ * @cond MQTTClient_internal
+ * @mainpage MQTT Client Library Internals
+ * In the beginning there was one MQTT C client library, MQTTClient, as implemented in MQTTClient.c
+ * This library was designed to be easy to use for applications which didn't mind if some of the calls
+ * blocked for a while. For instance, the MQTTClient_connect call will block until a successful
+ * connection has completed, or a connection has failed, which could be as long as the "connection
+ * timeout" interval, whose default is 30 seconds.
+ *
+ * However in mobile devices and other windowing environments, blocking on the GUI thread is a bad
+ * thing as it causes the user interface to freeze. Hence a new API, MQTTAsync, implemented
+ * in MQTTAsync.c, was devised. There are no blocking calls in this library, so it is well suited
+ * to GUI and mobile environments, at the expense of some extra complexity.
+ *
+ * Both libraries are designed to be sparing in the use of threads. So multiple client objects are
+ * handled by one or two threads, with a select call in Socket_getReadySocket(), used to determine
+ * when a socket has incoming data. This API is thread safe: functions may be called by multiple application
+ * threads, with the exception of ::MQTTClient_yield and ::MQTTClient_receive, which are intended
+ * for single threaded environments only.
+ *
+ * @endcond
+ * @cond MQTTClient_main
+ * @mainpage MQTT Client library for C
+ * &copy; Copyright IBM Corp. 2009, 2017
+ *
+ * @brief An MQTT client library in C.
+ *
+ * These pages describe the original more synchronous API which might be
+ * considered easier to use. Some of the calls will block. For the new
+ * totally asynchronous API where no calls block, which is especially suitable
+ * for use in windowed environments, see the
+ * <a href="../../MQTTAsync/html/index.html">MQTT C Client Asynchronous API Documentation</a>.
+ * The MQTTClient API is not thread safe, whereas the MQTTAsync API is.
+ *
+ * An MQTT client application connects to MQTT-capable servers.
+ * A typical client is responsible for collecting information from a telemetry
+ * device and publishing the information to the server. It can also subscribe
+ * to topics, receive messages, and use this information to control the
+ * telemetry device.
+ *
+ * MQTT clients implement the published MQTT v3 protocol. You can write your own
+ * API to the MQTT protocol using the programming language and platform of your
+ * choice. This can be time-consuming and error-prone.
+ *
+ * To simplify writing MQTT client applications, this library encapsulates
+ * the MQTT v3 protocol for you. Using this library enables a fully functional
+ * MQTT client application to be written in a few lines of code.
+ * The information presented here documents the API provided
+ * by the MQTT Client library for C.
+ *
+ * <b>Using the client</b><br>
+ * Applications that use the client library typically use a similar structure:
+ * <ul>
+ * <li>Create a client object</li>
+ * <li>Set the options to connect to an MQTT server</li>
+ * <li>Set up callback functions if multi-threaded (asynchronous mode)
+ * operation is being used (see @ref async).</li>
+ * <li>Subscribe to any topics the client needs to receive</li>
+ * <li>Repeat until finished:</li>
+ * <ul>
+ * <li>Publish any messages the client needs to</li>
+ * <li>Handle any incoming messages</li>
+ * </ul>
+ * <li>Disconnect the client</li>
+ * <li>Free any memory being used by the client</li>
+ * </ul>
+ * Some simple examples are shown here:
+ * <ul>
+ * <li>@ref pubsync</li>
+ * <li>@ref pubasync</li>
+ * <li>@ref subasync</li>
+ * </ul>
+ * Additional information about important concepts is provided here:
+ * <ul>
+ * <li>@ref async</li>
+ * <li>@ref wildcard</li>
+ * <li>@ref qos</li>
+ * <li>@ref tracing</li>
+ * </ul>
+ * @endcond
+ */
+
+/*
+/// @cond EXCLUDE
+*/
+#if defined(__cplusplus)
+ extern "C" {
+#endif
+#if !defined(MQTTCLIENT_H)
+#define MQTTCLIENT_H
+
+#if defined(WIN32) || defined(WIN64)
+ #define DLLImport __declspec(dllimport)
+ #define DLLExport __declspec(dllexport)
+#else
+ #define DLLImport extern
+ #define DLLExport __attribute__ ((visibility ("default")))
+#endif
+
+#include <stdio.h>
+/*
+/// @endcond
+*/
+
+#if !defined(NO_PERSISTENCE)
+#include "../paho-mqtt/MQTTClientPersistence.h"
+#endif
+
+/**
+ * Return code: No error. Indicates successful completion of an MQTT client
+ * operation.
+ */
+#define MQTTCLIENT_SUCCESS 0
+/**
+ * Return code: A generic error code indicating the failure of an MQTT client
+ * operation.
+ */
+#define MQTTCLIENT_FAILURE -1
+
+/* error code -2 is MQTTCLIENT_PERSISTENCE_ERROR */
+
+/**
+ * Return code: The client is disconnected.
+ */
+#define MQTTCLIENT_DISCONNECTED -3
+/**
+ * Return code: The maximum number of messages allowed to be simultaneously
+ * in-flight has been reached.
+ */
+#define MQTTCLIENT_MAX_MESSAGES_INFLIGHT -4
+/**
+ * Return code: An invalid UTF-8 string has been detected.
+ */
+#define MQTTCLIENT_BAD_UTF8_STRING -5
+/**
+ * Return code: A NULL parameter has been supplied when this is invalid.
+ */
+#define MQTTCLIENT_NULL_PARAMETER -6
+/**
+ * Return code: The topic has been truncated (the topic string includes
+ * embedded NULL characters). String functions will not access the full topic.
+ * Use the topic length value to access the full topic.
+ */
+#define MQTTCLIENT_TOPICNAME_TRUNCATED -7
+/**
+ * Return code: A structure parameter does not have the correct eyecatcher
+ * and version number.
+ */
+#define MQTTCLIENT_BAD_STRUCTURE -8
+/**
+ * Return code: A QoS value that falls outside of the acceptable range (0,1,2)
+ */
+#define MQTTCLIENT_BAD_QOS -9
+/**
+ * Return code: Attempting SSL connection using non-SSL version of library
+ */
+#define MQTTCLIENT_SSL_NOT_SUPPORTED -10
+/**
+ * Return code: protocol prefix in serverURI should be tcp:// or ssl://
+ */
+#define MQTTCLIENT_BAD_PROTOCOL -14
+
+/**
+ * Default MQTT version to connect with. Use 3.1.1 then fall back to 3.1
+ */
+#define MQTTVERSION_DEFAULT 0
+/**
+ * MQTT version to connect with: 3.1
+ */
+#define MQTTVERSION_3_1 3
+/**
+ * MQTT version to connect with: 3.1.1
+ */
+#define MQTTVERSION_3_1_1 4
+/**
+ * Bad return code from subscribe, as defined in the 3.1.1 specification
+ */
+#define MQTT_BAD_SUBSCRIBE 0x80
+
+/**
+ * Initialization options
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. Must be MQTG. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 */
+ int struct_version;
+ /** 1 = we do openssl init, 0 = leave it to the application */
+ int do_openssl_init;
+} MQTTClient_init_options;
+
+#define MQTTClient_init_options_initializer { {'M', 'Q', 'T', 'G'}, 0, 0 }
+
+/**
+ * Global init of mqtt library. Call once on program start to set global behaviour.
+ * do_openssl_init - if mqtt library should initialize OpenSSL (1) or rely on the caller to do it before using the library (0)
+ */
+DLLExport void MQTTClient_global_init(MQTTClient_init_options* inits);
+
+/**
+ * A handle representing an MQTT client. A valid client handle is available
+ * following a successful call to MQTTClient_create().
+ */
+typedef void* MQTTClient;
+/**
+ * A value representing an MQTT message. A delivery token is returned to the
+ * client application when a message is published. The token can then be used to
+ * check that the message was successfully delivered to its destination (see
+ * MQTTClient_publish(),
+ * MQTTClient_publishMessage(),
+ * MQTTClient_deliveryComplete(),
+ * MQTTClient_waitForCompletion() and
+ * MQTTClient_getPendingDeliveryTokens()).
+ */
+typedef int MQTTClient_deliveryToken;
+typedef int MQTTClient_token;
+
+/**
+ * A structure representing the payload and attributes of an MQTT message. The
+ * message topic is not part of this structure (see MQTTClient_publishMessage(),
+ * MQTTClient_publish(), MQTTClient_receive(), MQTTClient_freeMessage()
+ * and MQTTClient_messageArrived()).
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. must be MQTM. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 */
+ int struct_version;
+ /** The length of the MQTT message payload in bytes. */
+ int payloadlen;
+ /** A pointer to the payload of the MQTT message. */
+ void* payload;
+ /**
+ * The quality of service (QoS) assigned to the message.
+ * There are three levels of QoS:
+ * <DL>
+ * <DT><B>QoS0</B></DT>
+ * <DD>Fire and forget - the message may not be delivered</DD>
+ * <DT><B>QoS1</B></DT>
+ * <DD>At least once - the message will be delivered, but may be
+ * delivered more than once in some circumstances.</DD>
+ * <DT><B>QoS2</B></DT>
+ * <DD>Once and one only - the message will be delivered exactly once.</DD>
+ * </DL>
+ */
+ int qos;
+ /**
+ * The retained flag serves two purposes depending on whether the message
+ * it is associated with is being published or received.
+ *
+ * <b>retained = true</b><br>
+ * For messages being published, a true setting indicates that the MQTT
+ * server should retain a copy of the message. The message will then be
+ * transmitted to new subscribers to a topic that matches the message topic.
+ * For subscribers registering a new subscription, the flag being true
+ * indicates that the received message is not a new one, but one that has
+ * been retained by the MQTT server.
+ *
+ * <b>retained = false</b> <br>
+ * For publishers, this ndicates that this message should not be retained
+ * by the MQTT server. For subscribers, a false setting indicates this is
+ * a normal message, received as a result of it being published to the
+ * server.
+ */
+ int retained;
+ /**
+ * The dup flag indicates whether or not this message is a duplicate.
+ * It is only meaningful when receiving QoS1 messages. When true, the
+ * client application should take appropriate action to deal with the
+ * duplicate message.
+ */
+ int dup;
+ /** The message identifier is normally reserved for internal use by the
+ * MQTT client and server.
+ */
+ int msgid;
+} MQTTClient_message;
+
+#define MQTTClient_message_initializer { {'M', 'Q', 'T', 'M'}, 0, 0, NULL, 0, 0, 0, 0 }
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * receipt of messages. The function is registered with the client library by
+ * passing it as an argument to MQTTClient_setCallbacks(). It is
+ * called by the client library when a new message that matches a client
+ * subscription has been received from the server. This function is executed on
+ * a separate thread to the one on which the client application is running.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * MQTTClient_setCallbacks(), which contains any application-specific context.
+ * @param topicName The topic associated with the received message.
+ * @param topicLen The length of the topic if there are one
+ * more NULL characters embedded in <i>topicName</i>, otherwise <i>topicLen</i>
+ * is 0. If <i>topicLen</i> is 0, the value returned by <i>strlen(topicName)</i>
+ * can be trusted. If <i>topicLen</i> is greater than 0, the full topic name
+ * can be retrieved by accessing <i>topicName</i> as a byte array of length
+ * <i>topicLen</i>.
+ * @param message The MQTTClient_message structure for the received message.
+ * This structure contains the message payload and attributes.
+ * @return This function must return a boolean value indicating whether or not
+ * the message has been safely received by the client application. Returning
+ * true indicates that the message has been successfully handled.
+ * Returning false indicates that there was a problem. In this
+ * case, the client library will reinvoke MQTTClient_messageArrived() to
+ * attempt to deliver the message to the application again.
+ */
+typedef int MQTTClient_messageArrived(void* context, char* topicName, int topicLen, MQTTClient_message* message);
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * notification of delivery of messages. The function is registered with the
+ * client library by passing it as an argument to MQTTClient_setCallbacks().
+ * It is called by the client library after the client application has
+ * published a message to the server. It indicates that the necessary
+ * handshaking and acknowledgements for the requested quality of service (see
+ * MQTTClient_message.qos) have been completed. This function is executed on a
+ * separate thread to the one on which the client application is running.
+ * <b>Note:</b>MQTTClient_deliveryComplete() is not called when messages are
+ * published at QoS0.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * MQTTClient_setCallbacks(), which contains any application-specific context.
+ * @param dt The ::MQTTClient_deliveryToken associated with
+ * the published message. Applications can check that all messages have been
+ * correctly published by matching the delivery tokens returned from calls to
+ * MQTTClient_publish() and MQTTClient_publishMessage() with the tokens passed
+ * to this callback.
+ */
+typedef void MQTTClient_deliveryComplete(void* context, MQTTClient_deliveryToken dt);
+
+/**
+ * This is a callback function. The client application
+ * must provide an implementation of this function to enable asynchronous
+ * notification of the loss of connection to the server. The function is
+ * registered with the client library by passing it as an argument to
+ * MQTTClient_setCallbacks(). It is called by the client library if the client
+ * loses its connection to the server. The client application must take
+ * appropriate action, such as trying to reconnect or reporting the problem.
+ * This function is executed on a separate thread to the one on which the
+ * client application is running.
+ * @param context A pointer to the <i>context</i> value originally passed to
+ * MQTTClient_setCallbacks(), which contains any application-specific context.
+ * @param cause The reason for the disconnection.
+ * Currently, <i>cause</i> is always set to NULL.
+ */
+typedef void MQTTClient_connectionLost(void* context, char* cause);
+
+/**
+ * This function sets the callback functions for a specific client.
+ * If your client application doesn't use a particular callback, set the
+ * relevant parameter to NULL. Calling MQTTClient_setCallbacks() puts the
+ * client into multi-threaded mode. Any necessary message acknowledgements and
+ * status communications are handled in the background without any intervention
+ * from the client application. See @ref async for more information.
+ *
+ * <b>Note:</b> The MQTT client must be disconnected when this function is
+ * called.
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param context A pointer to any application-specific context. The
+ * the <i>context</i> pointer is passed to each of the callback functions to
+ * provide access to the context information in the callback.
+ * @param cl A pointer to an MQTTClient_connectionLost() callback
+ * function. You can set this to NULL if your application doesn't handle
+ * disconnections.
+ * @param ma A pointer to an MQTTClient_messageArrived() callback
+ * function. This callback function must be specified when you call
+ * MQTTClient_setCallbacks().
+ * @param dc A pointer to an MQTTClient_deliveryComplete() callback
+ * function. You can set this to NULL if your application publishes
+ * synchronously or if you do not want to check for successful delivery.
+ * @return ::MQTTCLIENT_SUCCESS if the callbacks were correctly set,
+ * ::MQTTCLIENT_FAILURE if an error occurred.
+ */
+DLLExport int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl,
+ MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc);
+
+
+/**
+ * This function creates an MQTT client ready for connection to the
+ * specified server and using the specified persistent storage (see
+ * MQTTClient_persistence). See also MQTTClient_destroy().
+ * @param handle A pointer to an ::MQTTClient handle. The handle is
+ * populated with a valid client reference following a successful return from
+ * this function.
+ * @param serverURI A null-terminated string specifying the server to
+ * which the client will connect. It takes the form <i>protocol://host:port</i>.
+ * Currently, <i>protocol</i> must be <i>tcp</i> or <i>ssl</i>.
+ * For <i>host</i>, you can
+ * specify either an IP address or a host name. For instance, to connect to
+ * a server running on the local machines with the default MQTT port, specify
+ * <i>tcp://localhost:1883</i>.
+ * @param clientId The client identifier passed to the server when the
+ * client connects to it. It is a null-terminated UTF-8 encoded string.
+ * @param persistence_type The type of persistence to be used by the client:
+ * <br>
+ * ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or
+ * system on which the client is running fails or is switched off, the current
+ * state of any in-flight messages is lost and some messages may not be
+ * delivered even at QoS1 and QoS2.
+ * <br>
+ * ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based)
+ * persistence mechanism. Status about in-flight messages is held in persistent
+ * storage and provides some protection against message loss in the case of
+ * unexpected failure.
+ * <br>
+ * ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence
+ * implementation. Using this type of persistence gives control of the
+ * persistence mechanism to the application. The application has to implement
+ * the MQTTClient_persistence interface.
+ * @param persistence_context If the application uses
+ * ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should
+ * be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it
+ * should be set to the location of the persistence directory (if set
+ * to NULL, the persistence directory used is the working directory).
+ * Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this
+ * argument to point to a valid MQTTClient_persistence structure.
+ * @return ::MQTTCLIENT_SUCCESS if the client is successfully created, otherwise
+ * an error code is returned.
+ */
+DLLExport int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId,
+ int persistence_type, void* persistence_context);
+
+/**
+ * MQTTClient_willOptions defines the MQTT "Last Will and Testament" (LWT) settings for
+ * the client. In the event that a client unexpectedly loses its connection to
+ * the server, the server publishes the LWT message to the LWT topic on
+ * behalf of the client. This allows other clients (subscribed to the LWT topic)
+ * to be made aware that the client has disconnected. To enable the LWT
+ * function for a specific client, a valid pointer to an MQTTClient_willOptions
+ * structure is passed in the MQTTClient_connectOptions structure used in the
+ * MQTTClient_connect() call that connects the client to the server. The pointer
+ * to MQTTClient_willOptions can be set to NULL if the LWT function is not
+ * required.
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. must be MQTW. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0 or 1
+ 0 means there is no binary payload option
+ */
+ int struct_version;
+ /** The LWT topic to which the LWT message will be published. */
+ const char* topicName;
+ /** The LWT payload in string form. */
+ const char* message;
+ /**
+ * The retained flag for the LWT message (see MQTTClient_message.retained).
+ */
+ int retained;
+ /**
+ * The quality of service setting for the LWT message (see
+ * MQTTClient_message.qos and @ref qos).
+ */
+ int qos;
+ /** The LWT payload in binary form. This is only checked and used if the message option is NULL */
+ struct
+ {
+ int len; /**< binary payload length */
+ const void* data; /**< binary payload data */
+ } payload;
+} MQTTClient_willOptions;
+
+#define MQTTClient_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 1, NULL, NULL, 0, 0, {0, NULL} }
+
+#define MQTT_SSL_VERSION_DEFAULT 0
+#define MQTT_SSL_VERSION_TLS_1_0 1
+#define MQTT_SSL_VERSION_TLS_1_1 2
+#define MQTT_SSL_VERSION_TLS_1_2 3
+
+/**
+* MQTTClient_sslProperties defines the settings to establish an SSL/TLS connection using the
+* OpenSSL library. It covers the following scenarios:
+* - Server authentication: The client needs the digital certificate of the server. It is included
+* in a store containting trusted material (also known as "trust store").
+* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In
+* addition to the digital certificate of the server in a trust store, the client will need its own
+* digital certificate and the private key used to sign its digital certificate stored in a "key store".
+* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed
+* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to
+* man-in-the-middle attacks.
+*/
+typedef struct
+{
+ /** The eyecatcher for this structure. Must be MQTS */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0, or 1 to enable TLS version selection. */
+ int struct_version;
+
+ /** The file in PEM format containing the public digital certificates trusted by the client. */
+ const char* trustStore;
+
+ /** The file in PEM format containing the public certificate chain of the client. It may also include
+ * the client's private key.
+ */
+ const char* keyStore;
+
+ /** If not included in the sslKeyStore, this setting points to the file in PEM format containing
+ * the client's private key.
+ */
+ const char* privateKey;
+ /** The password to load the client's privateKey if encrypted. */
+ const char* privateKeyPassword;
+
+ /**
+ * The list of cipher suites that the client will present to the server during the SSL handshake. For a
+ * full explanation of the cipher list format, please see the OpenSSL on-line documentation:
+ * http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
+ * If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding
+ * those offering no encryption- will be considered.
+ * This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance).
+ */
+ const char* enabledCipherSuites;
+
+ /** True/False option to enable verification of the server certificate **/
+ int enableServerCertAuth;
+
+ /** The SSL/TLS version to use. Specify one of MQTT_SSL_VERSION_DEFAULT (0),
+ * MQTT_SSL_VERSION_TLS_1_0 (1), MQTT_SSL_VERSION_TLS_1_1 (2) or MQTT_SSL_VERSION_TLS_1_2 (3).
+ * Only used if struct_version is >= 1.
+ */
+ int sslVersion;
+
+ /**
+ * Whether to carry out post-connect checks, including that a certificate
+ * matches the given host name.
+ * Exists only if struct_version >= 2
+ */
+ int verify;
+
+ /**
+ * From the OpenSSL documentation:
+ * If CApath is not NULL, it points to a directory containing CA certificates in PEM format.
+ * Exists only if struct_version >= 2
+ */
+ const char* CApath;
+
+} MQTTClient_SSLOptions;
+
+#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 2, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL }
+
+/**
+ * MQTTClient_connectOptions defines several settings that control the way the
+ * client connects to an MQTT server.
+ *
+ * <b>Note:</b> Default values are not defined for members of
+ * MQTTClient_connectOptions so it is good practice to specify all settings.
+ * If the MQTTClient_connectOptions structure is defined as an automatic
+ * variable, all members are set to random values and thus must be set by the
+ * client application. If the MQTTClient_connectOptions structure is defined
+ * as a static variable, initialization (in compliant compilers) sets all
+ * values to 0 (NULL for pointers). A #keepAliveInterval setting of 0 prevents
+ * correct operation of the client and so you <b>must</b> at least set a value
+ * for #keepAliveInterval.
+ */
+typedef struct
+{
+ /** The eyecatcher for this structure. must be MQTC. */
+ char struct_id[4];
+ /** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5.
+ * 0 signifies no SSL options and no serverURIs
+ * 1 signifies no serverURIs
+ * 2 signifies no MQTTVersion
+ * 3 signifies no returned values
+ * 4 signifies no binary password option
+ */
+ int struct_version;
+ /** The "keep alive" interval, measured in seconds, defines the maximum time
+ * that should pass without communication between the client and the server
+ * The client will ensure that at least one message travels across the
+ * network within each keep alive period. In the absence of a data-related
+ * message during the time period, the client sends a very small MQTT
+ * "ping" message, which the server will acknowledge. The keep alive
+ * interval enables the client to detect when the server is no longer
+ * available without having to wait for the long TCP/IP timeout.
+ */
+ int keepAliveInterval;
+ /**
+ * This is a boolean value. The cleansession setting controls the behaviour
+ * of both the client and the server at connection and disconnection time.
+ * The client and server both maintain session state information. This
+ * information is used to ensure "at least once" and "exactly once"
+ * delivery, and "exactly once" receipt of messages. Session state also
+ * includes subscriptions created by an MQTT client. You can choose to
+ * maintain or discard state information between sessions.
+ *
+ * When cleansession is true, the state information is discarded at
+ * connect and disconnect. Setting cleansession to false keeps the state
+ * information. When you connect an MQTT client application with
+ * MQTTClient_connect(), the client identifies the connection using the
+ * client identifier and the address of the server. The server checks
+ * whether session information for this client
+ * has been saved from a previous connection to the server. If a previous
+ * session still exists, and cleansession=true, then the previous session
+ * information at the client and server is cleared. If cleansession=false,
+ * the previous session is resumed. If no previous session exists, a new
+ * session is started.
+ */
+ int cleansession;
+ /**
+ * This is a boolean value that controls how many messages can be in-flight
+ * simultaneously. Setting <i>reliable</i> to true means that a published
+ * message must be completed (acknowledgements received) before another
+ * can be sent. Attempts to publish additional messages receive an
+ * ::MQTTCLIENT_MAX_MESSAGES_INFLIGHT return code. Setting this flag to
+ * false allows up to 10 messages to be in-flight. This can increase
+ * overall throughput in some circumstances.
+ */
+ int reliable;
+ /**
+ * This is a pointer to an MQTTClient_willOptions structure. If your
+ * application does not make use of the Last Will and Testament feature,
+ * set this pointer to NULL.
+ */
+ MQTTClient_willOptions* will;
+ /**
+ * MQTT servers that support the MQTT v3.1.1 protocol provide authentication
+ * and authorisation by user name and password. This is the user name
+ * parameter.
+ */
+ const char* username;
+ /**
+ * MQTT servers that support the MQTT v3.1.1 protocol provide authentication
+ * and authorisation by user name and password. This is the password
+ * parameter.
+ */
+ const char* password;
+ /**
+ * The time interval in seconds to allow a connect to complete.
+ */
+ int connectTimeout;
+ /**
+ * The time interval in seconds
+ */
+ int retryInterval;
+ /**
+ * This is a pointer to an MQTTClient_SSLOptions structure. If your
+ * application does not make use of SSL, set this pointer to NULL.
+ */
+ MQTTClient_SSLOptions* ssl;
+ /**
+ * The number of entries in the optional serverURIs array. Defaults to 0.
+ */
+ int serverURIcount;
+ /**
+ * An optional array of null-terminated strings specifying the servers to
+ * which the client will connect. Each string takes the form <i>protocol://host:port</i>.
+ * <i>protocol</i> must be <i>tcp</i> or <i>ssl</i>. For <i>host</i>, you can
+ * specify either an IP address or a host name. For instance, to connect to
+ * a server running on the local machines with the default MQTT port, specify
+ * <i>tcp://localhost:1883</i>.
+ * If this list is empty (the default), the server URI specified on MQTTClient_create()
+ * is used.
+ */
+ char* const* serverURIs;
+ /**
+ * Sets the version of MQTT to be used on the connect.
+ * MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that fails, fall back to 3.1
+ * MQTTVERSION_3_1 (3) = only try version 3.1
+ * MQTTVERSION_3_1_1 (4) = only try version 3.1.1
+ */
+ int MQTTVersion;
+ /**
+ * Returned from the connect when the MQTT version used to connect is 3.1.1
+ */
+ struct
+ {
+ const char* serverURI; /**< the serverURI connected to */
+ int MQTTVersion; /**< the MQTT version used to connect with */
+ int sessionPresent; /**< if the MQTT version is 3.1.1, the value of sessionPresent returned in the connack */
+ } returned;
+ /**
+ * Optional binary password. Only checked and used if the password option is NULL
+ */
+ struct {
+ int len; /**< binary password length */
+ const void* data; /**< binary password data */
+ } binarypwd;
+} MQTTClient_connectOptions;
+
+#define MQTTClient_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 5, 60, 1, 1, NULL, NULL, NULL, 30, 20, NULL, 0, NULL, 0, {NULL, 0, 0}, {0, NULL} }
+
+/**
+ * MQTTClient_libraryInfo is used to store details relating to the currently used
+ * library such as the version in use, the time it was built and relevant openSSL
+ * options.
+ * There is one static instance of this struct in MQTTClient.c
+ */
+
+typedef struct
+{
+ const char* name;
+ const char* value;
+} MQTTClient_nameValue;
+
+/**
+ * This function returns version information about the library.
+ * no trace information will be returned.
+ * @return an array of strings describing the library. The last entry is a NULL pointer.
+ */
+DLLExport MQTTClient_nameValue* MQTTClient_getVersionInfo(void);
+
+/**
+ * This function attempts to connect a previously-created client (see
+ * MQTTClient_create()) to an MQTT server using the specified options. If you
+ * want to enable asynchronous message and status notifications, you must call
+ * MQTTClient_setCallbacks() prior to MQTTClient_connect().
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param options A pointer to a valid MQTTClient_connectOptions
+ * structure.
+ * @return ::MQTTCLIENT_SUCCESS if the client successfully connects to the
+ * server. An error code is returned if the client was unable to connect to
+ * the server.
+ * Error codes greater than 0 are returned by the MQTT protocol:<br><br>
+ * <b>1</b>: Connection refused: Unacceptable protocol version<br>
+ * <b>2</b>: Connection refused: Identifier rejected<br>
+ * <b>3</b>: Connection refused: Server unavailable<br>
+ * <b>4</b>: Connection refused: Bad user name or password<br>
+ * <b>5</b>: Connection refused: Not authorized<br>
+ * <b>6-255</b>: Reserved for future use<br>
+ */
+DLLExport int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options);
+
+/**
+ * This function attempts to disconnect the client from the MQTT
+ * server. In order to allow the client time to complete handling of messages
+ * that are in-flight when this function is called, a timeout period is
+ * specified. When the timeout period has expired, the client disconnects even
+ * if there are still outstanding message acknowledgements.
+ * The next time the client connects to the same server, any QoS 1 or 2
+ * messages which have not completed will be retried depending on the
+ * cleansession settings for both the previous and the new connection (see
+ * MQTTClient_connectOptions.cleansession and MQTTClient_connect()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param timeout The client delays disconnection for up to this time (in
+ * milliseconds) in order to allow in-flight message transfers to complete.
+ * @return ::MQTTCLIENT_SUCCESS if the client successfully disconnects from
+ * the server. An error code is returned if the client was unable to disconnect
+ * from the server
+ */
+DLLExport int MQTTClient_disconnect(MQTTClient handle, int timeout);
+
+/**
+ * This function allows the client application to test whether or not a
+ * client is currently connected to the MQTT server.
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @return Boolean true if the client is connected, otherwise false.
+ */
+DLLExport int MQTTClient_isConnected(MQTTClient handle);
+
+
+/* Subscribe is synchronous. QoS list parameter is changed on return to granted QoSs.
+ Returns return code, MQTTCLIENT_SUCCESS == success, non-zero some sort of error (TBD) */
+
+/**
+ * This function attempts to subscribe a client to a single topic, which may
+ * contain wildcards (see @ref wildcard). This call also specifies the
+ * @ref qos requested for the subscription
+ * (see also MQTTClient_subscribeMany()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param topic The subscription topic, which may include wildcards.
+ * @param qos The requested quality of service for the subscription.
+ * @return ::MQTTCLIENT_SUCCESS if the subscription request is successful.
+ * An error code is returned if there was a problem registering the
+ * subscription.
+ */
+DLLExport int MQTTClient_subscribe(MQTTClient handle, const char* topic, int qos);
+
+/**
+ * This function attempts to subscribe a client to a list of topics, which may
+ * contain wildcards (see @ref wildcard). This call also specifies the
+ * @ref qos requested for each topic (see also MQTTClient_subscribe()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param count The number of topics for which the client is requesting
+ * subscriptions.
+ * @param topic An array (of length <i>count</i>) of pointers to
+ * topics, each of which may include wildcards.
+ * @param qos An array (of length <i>count</i>) of @ref qos
+ * values. qos[n] is the requested QoS for topic[n].
+ * @return ::MQTTCLIENT_SUCCESS if the subscription request is successful.
+ * An error code is returned if there was a problem registering the
+ * subscriptions.
+ */
+DLLExport int MQTTClient_subscribeMany(MQTTClient handle, int count, char* const* topic, int* qos);
+
+/**
+ * This function attempts to remove an existing subscription made by the
+ * specified client.
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param topic The topic for the subscription to be removed, which may
+ * include wildcards (see @ref wildcard).
+ * @return ::MQTTCLIENT_SUCCESS if the subscription is removed.
+ * An error code is returned if there was a problem removing the
+ * subscription.
+ */
+DLLExport int MQTTClient_unsubscribe(MQTTClient handle, const char* topic);
+
+/**
+ * This function attempts to remove existing subscriptions to a list of topics
+ * made by the specified client.
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param count The number subscriptions to be removed.
+ * @param topic An array (of length <i>count</i>) of pointers to the topics of
+ * the subscriptions to be removed, each of which may include wildcards.
+ * @return ::MQTTCLIENT_SUCCESS if the subscriptions are removed.
+ * An error code is returned if there was a problem removing the subscriptions.
+ */
+DLLExport int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char* const* topic);
+
+/**
+ * This function attempts to publish a message to a given topic (see also
+ * MQTTClient_publishMessage()). An ::MQTTClient_deliveryToken is issued when
+ * this function returns successfully. If the client application needs to
+ * test for succesful delivery of QoS1 and QoS2 messages, this can be done
+ * either asynchronously or synchronously (see @ref async,
+ * ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param topicName The topic associated with this message.
+ * @param payloadlen The length of the payload in bytes.
+ * @param payload A pointer to the byte array payload of the message.
+ * @param qos The @ref qos of the message.
+ * @param retained The retained flag for the message.
+ * @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated
+ * with a token representing the message when the function returns
+ * successfully. If your application does not use delivery tokens, set this
+ * argument to NULL.
+ * @return ::MQTTCLIENT_SUCCESS if the message is accepted for publication.
+ * An error code is returned if there was a problem accepting the message.
+ */
+DLLExport int MQTTClient_publish(MQTTClient handle, const char* topicName, int payloadlen, void* payload, int qos, int retained,
+ MQTTClient_deliveryToken* dt);
+/**
+ * This function attempts to publish a message to a given topic (see also
+ * MQTTClient_publish()). An ::MQTTClient_deliveryToken is issued when
+ * this function returns successfully. If the client application needs to
+ * test for succesful delivery of QoS1 and QoS2 messages, this can be done
+ * either asynchronously or synchronously (see @ref async,
+ * ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param topicName The topic associated with this message.
+ * @param msg A pointer to a valid MQTTClient_message structure containing
+ * the payload and attributes of the message to be published.
+ * @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated
+ * with a token representing the message when the function returns
+ * successfully. If your application does not use delivery tokens, set this
+ * argument to NULL.
+ * @return ::MQTTCLIENT_SUCCESS if the message is accepted for publication.
+ * An error code is returned if there was a problem accepting the message.
+ */
+DLLExport int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* msg, MQTTClient_deliveryToken* dt);
+
+
+/**
+ * This function is called by the client application to synchronize execution
+ * of the main thread with completed publication of a message. When called,
+ * MQTTClient_waitForCompletion() blocks execution until the message has been
+ * successful delivered or the specified timeout has expired. See @ref async.
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param dt The ::MQTTClient_deliveryToken that represents the message being
+ * tested for successful delivery. Delivery tokens are issued by the
+ * publishing functions MQTTClient_publish() and MQTTClient_publishMessage().
+ * @param timeout The maximum time to wait in milliseconds.
+ * @return ::MQTTCLIENT_SUCCESS if the message was successfully delivered.
+ * An error code is returned if the timeout expires or there was a problem
+ * checking the token.
+ */
+DLLExport int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken dt, unsigned long timeout);
+
+
+/**
+ * This function sets a pointer to an array of delivery tokens for
+ * messages that are currently in-flight (pending completion).
+ *
+ * <b>Important note:</b> The memory used to hold the array of tokens is
+ * malloc()'d in this function. The client application is responsible for
+ * freeing this memory when it is no longer required.
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param tokens The address of a pointer to an ::MQTTClient_deliveryToken.
+ * When the function returns successfully, the pointer is set to point to an
+ * array of tokens representing messages pending completion. The last member of
+ * the array is set to -1 to indicate there are no more tokens. If no tokens
+ * are pending, the pointer is set to NULL.
+ * @return ::MQTTCLIENT_SUCCESS if the function returns successfully.
+ * An error code is returned if there was a problem obtaining the list of
+ * pending tokens.
+ */
+DLLExport int MQTTClient_getPendingDeliveryTokens(MQTTClient handle, MQTTClient_deliveryToken **tokens);
+
+/**
+ * When implementing a single-threaded client, call this function periodically
+ * to allow processing of message retries and to send MQTT keepalive pings.
+ * If the application is calling MQTTClient_receive() regularly, then it is
+ * not necessary to call this function.
+ */
+DLLExport void MQTTClient_yield(void);
+
+/**
+ * This function performs a synchronous receive of incoming messages. It should
+ * be used only when the client application has not set callback methods to
+ * support asynchronous receipt of messages (see @ref async and
+ * MQTTClient_setCallbacks()). Using this function allows a single-threaded
+ * client subscriber application to be written. When called, this function
+ * blocks until the next message arrives or the specified timeout expires
+ *(see also MQTTClient_yield()).
+ *
+ * <b>Important note:</b> The application must free() the memory allocated
+ * to the topic and the message when processing is complete (see
+ * MQTTClient_freeMessage()).
+ * @param handle A valid client handle from a successful call to
+ * MQTTClient_create().
+ * @param topicName The address of a pointer to a topic. This function
+ * allocates the memory for the topic and returns it to the application
+ * by setting <i>topicName</i> to point to the topic.
+ * @param topicLen The length of the topic. If the return code from this
+ * function is ::MQTTCLIENT_TOPICNAME_TRUNCATED, the topic contains embedded
+ * NULL characters and the full topic should be retrieved by using
+ * <i>topicLen</i>.
+ * @param message The address of a pointer to the received message. This
+ * function allocates the memory for the message and returns it to the
+ * application by setting <i>message</i> to point to the received message.
+ * The pointer is set to NULL if the timeout expires.
+ * @param timeout The length of time to wait for a message in milliseconds.
+ * @return ::MQTTCLIENT_SUCCESS or ::MQTTCLIENT_TOPICNAME_TRUNCATED if a
+ * message is received. ::MQTTCLIENT_SUCCESS can also indicate that the
+ * timeout expired, in which case <i>message</i> is NULL. An error code is
+ * returned if there was a problem trying to receive a message.
+ */
+DLLExport int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTClient_message** message,
+ unsigned long timeout);
+
+/**
+ * This function frees memory allocated to an MQTT message, including the
+ * additional memory allocated to the message payload. The client application
+ * calls this function when the message has been fully processed. <b>Important
+ * note:</b> This function does not free the memory allocated to a message
+ * topic string. It is the responsibility of the client application to free
+ * this memory using the MQTTClient_free() library function.
+ * @param msg The address of a pointer to the ::MQTTClient_message structure
+ * to be freed.
+ */
+DLLExport void MQTTClient_freeMessage(MQTTClient_message** msg);
+
+/**
+ * This function frees memory allocated by the MQTT C client library, especially the
+ * topic name. This is needed on Windows when the client libary and application
+ * program have been compiled with different versions of the C compiler. It is
+ * thus good policy to always use this function when freeing any MQTT C client-
+ * allocated memory.
+ * @param ptr The pointer to the client library storage to be freed.
+ */
+DLLExport void MQTTClient_free(void* ptr);
+
+/**
+ * This function frees the memory allocated to an MQTT client (see
+ * MQTTClient_create()). It should be called when the client is no longer
+ * required.
+ * @param handle A pointer to the handle referring to the ::MQTTClient
+ * structure to be freed.
+ */
+DLLExport void MQTTClient_destroy(MQTTClient* handle);
+
+#endif
+#ifdef __cplusplus
+ }
+#endif
+
+/**
+ * @cond MQTTClient_main
+ * @page async Asynchronous vs synchronous client applications
+ * The client library supports two modes of operation. These are referred to
+ * as <b>synchronous</b> and <b>asynchronous</b> modes. If your application
+ * calls MQTTClient_setCallbacks(), this puts the client into asynchronous
+ * mode, otherwise it operates in synchronous mode.
+ *
+ * In synchronous mode, the client application runs on a single thread.
+ * Messages are published using the MQTTClient_publish() and
+ * MQTTClient_publishMessage() functions. To determine that a QoS1 or QoS2
+ * (see @ref qos) message has been successfully delivered, the application
+ * must call the MQTTClient_waitForCompletion() function. An example showing
+ * synchronous publication is shown in @ref pubsync. Receiving messages in
+ * synchronous mode uses the MQTTClient_receive() function. Client applications
+ * must call either MQTTClient_receive() or MQTTClient_yield() relatively
+ * frequently in order to allow processing of acknowledgements and the MQTT
+ * "pings" that keep the network connection to the server alive.
+ *
+ * In asynchronous mode, the client application runs on several threads. The
+ * main program calls functions in the client library to publish and subscribe,
+ * just as for the synchronous mode. Processing of handshaking and maintaining
+ * the network connection is performed in the background, however.
+ * Notifications of status and message reception are provided to the client
+ * application using callbacks registered with the library by the call to
+ * MQTTClient_setCallbacks() (see MQTTClient_messageArrived(),
+ * MQTTClient_connectionLost() and MQTTClient_deliveryComplete()).
+ * This API is not thread safe however - it is not possible to call it from multiple
+ * threads without synchronization. You can use the MQTTAsync API for that.
+ *
+ * @page wildcard Subscription wildcards
+ * Every MQTT message includes a topic that classifies it. MQTT servers use
+ * topics to determine which subscribers should receive messages published to
+ * the server.
+ *
+ * Consider the server receiving messages from several environmental sensors.
+ * Each sensor publishes its measurement data as a message with an associated
+ * topic. Subscribing applications need to know which sensor originally
+ * published each received message. A unique topic is thus used to identify
+ * each sensor and measurement type. Topics such as SENSOR1TEMP,
+ * SENSOR1HUMIDITY, SENSOR2TEMP and so on achieve this but are not very
+ * flexible. If additional sensors are added to the system at a later date,
+ * subscribing applications must be modified to receive them.
+ *
+ * To provide more flexibility, MQTT supports a hierarchical topic namespace.
+ * This allows application designers to organize topics to simplify their
+ * management. Levels in the hierarchy are delimited by the '/' character,
+ * such as SENSOR/1/HUMIDITY. Publishers and subscribers use these
+ * hierarchical topics as already described.
+ *
+ * For subscriptions, two wildcard characters are supported:
+ * <ul>
+ * <li>A '#' character represents a complete sub-tree of the hierarchy and
+ * thus must be the last character in a subscription topic string, such as
+ * SENSOR/#. This will match any topic starting with SENSOR/, such as
+ * SENSOR/1/TEMP and SENSOR/2/HUMIDITY.</li>
+ * <li> A '+' character represents a single level of the hierarchy and is
+ * used between delimiters. For example, SENSOR/+/TEMP will match
+ * SENSOR/1/TEMP and SENSOR/2/TEMP.</li>
+ * </ul>
+ * Publishers are not allowed to use the wildcard characters in their topic
+ * names.
+ *
+ * Deciding on your topic hierarchy is an important step in your system design.
+ *
+ * @page qos Quality of service
+ * The MQTT protocol provides three qualities of service for delivering
+ * messages between clients and servers: "at most once", "at least once" and
+ * "exactly once".
+ *
+ * Quality of service (QoS) is an attribute of an individual message being
+ * published. An application sets the QoS for a specific message by setting the
+ * MQTTClient_message.qos field to the required value.
+ *
+ * A subscribing client can set the maximum quality of service a server uses
+ * to send messages that match the client subscriptions. The
+ * MQTTClient_subscribe() and MQTTClient_subscribeMany() functions set this
+ * maximum. The QoS of a message forwarded to a subscriber thus might be
+ * different to the QoS given to the message by the original publisher.
+ * The lower of the two values is used to forward a message.
+ *
+ * The three levels are:
+ *
+ * <b>QoS0, At most once:</b> The message is delivered at most once, or it
+ * may not be delivered at all. Its delivery across the network is not
+ * acknowledged. The message is not stored. The message could be lost if the
+ * client is disconnected, or if the server fails. QoS0 is the fastest mode of
+ * transfer. It is sometimes called "fire and forget".
+ *
+ * The MQTT protocol does not require servers to forward publications at QoS0
+ * to a client. If the client is disconnected at the time the server receives
+ * the publication, the publication might be discarded, depending on the
+ * server implementation.
+ *
+ * <b>QoS1, At least once:</b> The message is always delivered at least once.
+ * It might be delivered multiple times if there is a failure before an
+ * acknowledgment is received by the sender. The message must be stored
+ * locally at the sender, until the sender receives confirmation that the
+ * message has been published by the receiver. The message is stored in case
+ * the message must be sent again.
+ *
+ * <b>QoS2, Exactly once:</b> The message is always delivered exactly once.
+ * The message must be stored locally at the sender, until the sender receives
+ * confirmation that the message has been published by the receiver. The
+ * message is stored in case the message must be sent again. QoS2 is the
+ * safest, but slowest mode of transfer. A more sophisticated handshaking
+ * and acknowledgement sequence is used than for QoS1 to ensure no duplication
+ * of messages occurs.
+ * @page pubsync Synchronous publication example
+@code
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "MQTTClient.h"
+
+#define ADDRESS "tcp://localhost:1883"
+#define CLIENTID "ExampleClientPub"
+#define TOPIC "MQTT Examples"
+#define PAYLOAD "Hello World!"
+#define QOS 1
+#define TIMEOUT 10000L
+
+int main(int argc, char* argv[])
+{
+ MQTTClient client;
+ MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
+ MQTTClient_message pubmsg = MQTTClient_message_initializer;
+ MQTTClient_deliveryToken token;
+ int rc;
+
+ MQTTClient_create(&client, ADDRESS, CLIENTID,
+ MQTTCLIENT_PERSISTENCE_NONE, NULL);
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+
+ if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
+ {
+ printf("Failed to connect, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+ pubmsg.payload = PAYLOAD;
+ pubmsg.payloadlen = strlen(PAYLOAD);
+ pubmsg.qos = QOS;
+ pubmsg.retained = 0;
+ MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
+ printf("Waiting for up to %d seconds for publication of %s\n"
+ "on topic %s for client with ClientID: %s\n",
+ (int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID);
+ rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
+ printf("Message with delivery token %d delivered\n", token);
+ MQTTClient_disconnect(client, 10000);
+ MQTTClient_destroy(&client);
+ return rc;
+}
+
+ * @endcode
+ *
+ * @page pubasync Asynchronous publication example
+@code{.c}
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "MQTTClient.h"
+
+#define ADDRESS "tcp://localhost:1883"
+#define CLIENTID "ExampleClientPub"
+#define TOPIC "MQTT Examples"
+#define PAYLOAD "Hello World!"
+#define QOS 1
+#define TIMEOUT 10000L
+
+volatile MQTTClient_deliveryToken deliveredtoken;
+
+void delivered(void *context, MQTTClient_deliveryToken dt)
+{
+ printf("Message with token value %d delivery confirmed\n", dt);
+ deliveredtoken = dt;
+}
+
+int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
+{
+ int i;
+ char* payloadptr;
+
+ printf("Message arrived\n");
+ printf(" topic: %s\n", topicName);
+ printf(" message: ");
+
+ payloadptr = message->payload;
+ for(i=0; i<message->payloadlen; i++)
+ {
+ putchar(*payloadptr++);
+ }
+ putchar('\n');
+ MQTTClient_freeMessage(&message);
+ MQTTClient_free(topicName);
+ return 1;
+}
+
+void connlost(void *context, char *cause)
+{
+ printf("\nConnection lost\n");
+ printf(" cause: %s\n", cause);
+}
+
+int main(int argc, char* argv[])
+{
+ MQTTClient client;
+ MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
+ MQTTClient_message pubmsg = MQTTClient_message_initializer;
+ MQTTClient_deliveryToken token;
+ int rc;
+
+ MQTTClient_create(&client, ADDRESS, CLIENTID,
+ MQTTCLIENT_PERSISTENCE_NONE, NULL);
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+
+ MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
+
+ if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
+ {
+ printf("Failed to connect, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+ pubmsg.payload = PAYLOAD;
+ pubmsg.payloadlen = strlen(PAYLOAD);
+ pubmsg.qos = QOS;
+ pubmsg.retained = 0;
+ deliveredtoken = 0;
+ MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
+ printf("Waiting for publication of %s\n"
+ "on topic %s for client with ClientID: %s\n",
+ PAYLOAD, TOPIC, CLIENTID);
+ while(deliveredtoken != token);
+ MQTTClient_disconnect(client, 10000);
+ MQTTClient_destroy(&client);
+ return rc;
+}
+
+ * @endcode
+ * @page subasync Asynchronous subscription example
+@code
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "MQTTClient.h"
+
+#define ADDRESS "tcp://localhost:1883"
+#define CLIENTID "ExampleClientSub"
+#define TOPIC "MQTT Examples"
+#define PAYLOAD "Hello World!"
+#define QOS 1
+#define TIMEOUT 10000L
+
+volatile MQTTClient_deliveryToken deliveredtoken;
+
+void delivered(void *context, MQTTClient_deliveryToken dt)
+{
+ printf("Message with token value %d delivery confirmed\n", dt);
+ deliveredtoken = dt;
+}
+
+int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
+{
+ int i;
+ char* payloadptr;
+
+ printf("Message arrived\n");
+ printf(" topic: %s\n", topicName);
+ printf(" message: ");
+
+ payloadptr = message->payload;
+ for(i=0; i<message->payloadlen; i++)
+ {
+ putchar(*payloadptr++);
+ }
+ putchar('\n');
+ MQTTClient_freeMessage(&message);
+ MQTTClient_free(topicName);
+ return 1;
+}
+
+void connlost(void *context, char *cause)
+{
+ printf("\nConnection lost\n");
+ printf(" cause: %s\n", cause);
+}
+
+int main(int argc, char* argv[])
+{
+ MQTTClient client;
+ MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
+ int rc;
+ int ch;
+
+ MQTTClient_create(&client, ADDRESS, CLIENTID,
+ MQTTCLIENT_PERSISTENCE_NONE, NULL);
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+
+ MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
+
+ if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
+ {
+ printf("Failed to connect, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+ printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
+ "Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
+ MQTTClient_subscribe(client, TOPIC, QOS);
+
+ do
+ {
+ ch = getchar();
+ } while(ch!='Q' && ch != 'q');
+
+ MQTTClient_disconnect(client, 10000);
+ MQTTClient_destroy(&client);
+ return rc;
+}
+
+ * @endcode
+ * @page tracing Tracing
+ *
+ * Runtime tracing is controlled by environment variables.
+ *
+ * Tracing is switched on by setting MQTT_C_CLIENT_TRACE. A value of ON, or stdout, prints to
+ * stdout, any other value is interpreted as a file name to use.
+ *
+ * The amount of trace detail is controlled with the MQTT_C_CLIENT_TRACE_LEVEL environment
+ * variable - valid values are ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM
+ * (from least to most verbose).
+ *
+ * The variable MQTT_C_CLIENT_TRACE_MAX_LINES limits the number of lines of trace that are output
+ * to a file. Two files are used at most, when they are full, the last one is overwritten with the
+ * new trace entries. The default size is 1000 lines.
+ *
+ * ### MQTT Packet Tracing
+ *
+ * A feature that can be very useful is printing the MQTT packets that are sent and received. To
+ * achieve this, use the following environment variable settings:
+ * @code
+ MQTT_C_CLIENT_TRACE=ON
+ MQTT_C_CLIENT_TRACE_LEVEL=PROTOCOL
+ * @endcode
+ * The output you should see looks like this:
+ * @code
+ 20130528 155936.813 3 stdout-subscriber -> CONNECT cleansession: 1 (0)
+ 20130528 155936.813 3 stdout-subscriber <- CONNACK rc: 0
+ 20130528 155936.813 3 stdout-subscriber -> SUBSCRIBE msgid: 1 (0)
+ 20130528 155936.813 3 stdout-subscriber <- SUBACK msgid: 1
+ 20130528 155941.818 3 stdout-subscriber -> DISCONNECT (0)
+ * @endcode
+ * where the fields are:
+ * 1. date
+ * 2. time
+ * 3. socket number
+ * 4. client id
+ * 5. direction (-> from client to server, <- from server to client)
+ * 6. packet details
+ *
+ * ### Default Level Tracing
+ *
+ * This is an extract of a default level trace of a call to connect:
+ * @code
+ 19700101 010000.000 (1152206656) (0)> MQTTClient_connect:893
+ 19700101 010000.000 (1152206656) (1)> MQTTClient_connectURI:716
+ 20130528 160447.479 Connecting to serverURI localhost:1883
+ 20130528 160447.479 (1152206656) (2)> MQTTProtocol_connect:98
+ 20130528 160447.479 (1152206656) (3)> MQTTProtocol_addressPort:48
+ 20130528 160447.479 (1152206656) (3)< MQTTProtocol_addressPort:73
+ 20130528 160447.479 (1152206656) (3)> Socket_new:599
+ 20130528 160447.479 New socket 4 for localhost, port 1883
+ 20130528 160447.479 (1152206656) (4)> Socket_addSocket:163
+ 20130528 160447.479 (1152206656) (5)> Socket_setnonblocking:73
+ 20130528 160447.479 (1152206656) (5)< Socket_setnonblocking:78 (0)
+ 20130528 160447.479 (1152206656) (4)< Socket_addSocket:176 (0)
+ 20130528 160447.479 (1152206656) (4)> Socket_error:95
+ 20130528 160447.479 (1152206656) (4)< Socket_error:104 (115)
+ 20130528 160447.479 Connect pending
+ 20130528 160447.479 (1152206656) (3)< Socket_new:683 (115)
+ 20130528 160447.479 (1152206656) (2)< MQTTProtocol_connect:131 (115)
+ * @endcode
+ * where the fields are:
+ * 1. date
+ * 2. time
+ * 3. thread id
+ * 4. function nesting level
+ * 5. function entry (>) or exit (<)
+ * 6. function name : line of source code file
+ * 7. return value (if there is one)
+ *
+ * ### Memory Allocation Tracing
+ *
+ * Setting the trace level to maximum causes memory allocations and frees to be traced along with
+ * the default trace entries, with messages like the following:
+ * @code
+ 20130528 161819.657 Allocating 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 177 ptr 0x179f930
+
+ 20130528 161819.657 Freeing 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 201, heap use now 896 bytes
+ * @endcode
+ * When the last MQTT client object is destroyed, if the trace is being recorded
+ * and all memory allocated by the client library has not been freed, an error message will be
+ * written to the trace. This can help with fixing memory leaks. The message will look like this:
+ * @code
+ 20130528 163909.208 Some memory not freed at shutdown, possible memory leak
+ 20130528 163909.208 Heap scan start, total 880 bytes
+ 20130528 163909.208 Heap element size 32, line 354, file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c, ptr 0x260cb00
+ 20130528 163909.208 Content
+ 20130528 163909.209 Heap scan end
+ * @endcode
+ * @endcond
+ */
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTClientPersistence.h b/tts-mqtt-test/src/paho-mqtt/MQTTClientPersistence.h
new file mode 100644
index 0000000..4c9014d
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTClientPersistence.h
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2012 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief This structure represents a persistent data store, used to store
+ * outbound and inbound messages, in order to achieve reliable messaging.
+ *
+ * The MQTT Client persists QoS1 and QoS2 messages in order to meet the
+ * assurances of delivery associated with these @ref qos levels. The messages
+ * are saved in persistent storage
+ * The type and context of the persistence implementation are specified when
+ * the MQTT client is created (see MQTTClient_create()). The default
+ * persistence type (::MQTTCLIENT_PERSISTENCE_DEFAULT) uses a file system-based
+ * persistence mechanism. The <i>persistence_context</i> argument passed to
+ * MQTTClient_create() when using the default peristence is a string
+ * representing the location of the persistence directory. If the context
+ * argument is NULL, the working directory will be used.
+ *
+ * To use memory-based persistence, an application passes
+ * ::MQTTCLIENT_PERSISTENCE_NONE as the <i>persistence_type</i> to
+ * MQTTClient_create(). This can lead to message loss in certain situations,
+ * but can be appropriate in some cases (see @ref qos).
+ *
+ * Client applications can provide their own persistence mechanism by passing
+ * ::MQTTCLIENT_PERSISTENCE_USER as the <i>persistence_type</i>. To implement a
+ * custom persistence mechanism, the application must pass an initialized
+ * ::MQTTClient_persistence structure as the <i>persistence_context</i>
+ * argument to MQTTClient_create().
+ *
+ * If the functions defined return an ::MQTTCLIENT_PERSISTENCE_ERROR then the
+ * state of the persisted data should remain as it was prior to the function
+ * being called. For example, if Persistence_put() returns
+ * ::MQTTCLIENT_PERSISTENCE_ERROR, then it is assumed tha tthe persistent store
+ * does not contain the data that was passed to the function. Similarly, if
+ * Persistence_remove() returns ::MQTTCLIENT_PERSISTENCE_ERROR then it is
+ * assumed that the data to be removed is still held in the persistent store.
+ *
+ * It is up to the persistence implementation to log any error information that
+ * may be required to diagnose a persistence mechanism failure.
+ */
+
+/*
+/// @cond EXCLUDE
+*/
+#if !defined(MQTTCLIENTPERSISTENCE_H)
+#define MQTTCLIENTPERSISTENCE_H
+/*
+/// @endcond
+*/
+
+/**
+ * This <i>persistence_type</i> value specifies the default file system-based
+ * persistence mechanism (see MQTTClient_create()).
+ */
+#define MQTTCLIENT_PERSISTENCE_DEFAULT 0
+/**
+ * This <i>persistence_type</i> value specifies a memory-based
+ * persistence mechanism (see MQTTClient_create()).
+ */
+#define MQTTCLIENT_PERSISTENCE_NONE 1
+/**
+ * This <i>persistence_type</i> value specifies an application-specific
+ * persistence mechanism (see MQTTClient_create()).
+ */
+#define MQTTCLIENT_PERSISTENCE_USER 2
+
+/**
+ * Application-specific persistence functions must return this error code if
+ * there is a problem executing the function.
+ */
+#define MQTTCLIENT_PERSISTENCE_ERROR -2
+
+/**
+ * @brief Initialize the persistent store.
+ *
+ * Either open the existing persistent store for this client ID or create a new
+ * one if one doesn't exist. If the persistent store is already open, return
+ * without taking any action.
+ *
+ * An application can use the same client identifier to connect to many
+ * different servers. The <i>clientid</i> in conjunction with the
+ * <i>serverURI</i> uniquely identifies the persistence store required.
+ *
+ * @param handle The address of a pointer to a handle for this persistence
+ * implementation. This function must set handle to a valid reference to the
+ * persistence following a successful return.
+ * The handle pointer is passed as an argument to all the other
+ * persistence functions. It may include the context parameter and/or any other
+ * data for use by the persistence functions.
+ * @param clientID The client identifier for which the persistent store should
+ * be opened.
+ * @param serverURI The connection string specified when the MQTT client was
+ * created (see MQTTClient_create()).
+ * @param context A pointer to any data required to initialize the persistent
+ * store (see ::MQTTClient_persistence).
+ * @return Return 0 if the function completes successfully, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_open)(void** handle, const char* clientID, const char* serverURI, void* context);
+
+/**
+ * @brief Close the persistent store referred to by the handle.
+ *
+ * @param handle The handle pointer from a successful call to
+ * Persistence_open().
+ * @return Return 0 if the function completes successfully, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_close)(void* handle);
+
+/**
+ * @brief Put the specified data into the persistent store.
+ *
+ * @param handle The handle pointer from a successful call to
+ * Persistence_open().
+ * @param key A string used as the key for the data to be put in the store. The
+ * key is later used to retrieve data from the store with Persistence_get().
+ * @param bufcount The number of buffers to write to the persistence store.
+ * @param buffers An array of pointers to the data buffers associated with
+ * this <i>key</i>.
+ * @param buflens An array of lengths of the data buffers. <i>buflen[n]</i>
+ * gives the length of <i>buffer[n]</i>.
+ * @return Return 0 if the function completes successfully, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_put)(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
+
+/**
+ * @brief Retrieve the specified data from the persistent store.
+ *
+ * @param handle The handle pointer from a successful call to
+ * Persistence_open().
+ * @param key A string that is the key for the data to be retrieved. This is
+ * the same key used to save the data to the store with Persistence_put().
+ * @param buffer The address of a pointer to a buffer. This function sets the
+ * pointer to point at the retrieved data, if successful.
+ * @param buflen The address of an int that is set to the length of
+ * <i>buffer</i> by this function if successful.
+ * @return Return 0 if the function completes successfully, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_get)(void* handle, char* key, char** buffer, int* buflen);
+
+/**
+ * @brief Remove the data for the specified key from the store.
+ *
+ * @param handle The handle pointer from a successful call to
+ * Persistence_open().
+ * @param key A string that is the key for the data to be removed from the
+ * store. This is the same key used to save the data to the store with
+ * Persistence_put().
+ * @return Return 0 if the function completes successfully, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_remove)(void* handle, char* key);
+
+/**
+ * @brief Returns the keys in this persistent data store.
+ *
+ * @param handle The handle pointer from a successful call to
+ * Persistence_open().
+ * @param keys The address of a pointer to pointers to strings. Assuming
+ * successful execution, this function allocates memory to hold the returned
+ * keys (strings used to store the data with Persistence_put()). It also
+ * allocates memory to hold an array of pointers to these strings. <i>keys</i>
+ * is set to point to the array of pointers to strings.
+ * @param nkeys A pointer to the number of keys in this persistent data store.
+ * This function sets the number of keys, if successful.
+ * @return Return 0 if the function completes successfully, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_keys)(void* handle, char*** keys, int* nkeys);
+
+/**
+ * @brief Clears the persistence store, so that it no longer contains any
+ * persisted data.
+ *
+ * @param handle The handle pointer from a successful call to
+ * Persistence_open().
+ * @return Return 0 if the function completes successfully, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_clear)(void* handle);
+
+/**
+ * @brief Returns whether any data has been persisted using the specified key.
+ *
+ * @param handle The handle pointer from a successful call to
+ * Persistence_open().
+ * @param key The string to be tested for existence in the store.
+ * @return Return 0 if the key was found in the store, otherwise return
+ * ::MQTTCLIENT_PERSISTENCE_ERROR.
+ */
+typedef int (*Persistence_containskey)(void* handle, char* key);
+
+/**
+ * @brief A structure containing the function pointers to a persistence
+ * implementation and the context or state that will be shared across all
+ * the persistence functions.
+ */
+typedef struct {
+ /**
+ * A pointer to any data required to initialize the persistent store.
+ */
+ void* context;
+ /**
+ * A function pointer to an implementation of Persistence_open().
+ */
+ Persistence_open popen;
+ /**
+ * A function pointer to an implementation of Persistence_close().
+ */
+ Persistence_close pclose;
+ /**
+ * A function pointer to an implementation of Persistence_put().
+ */
+ Persistence_put pput;
+ /**
+ * A function pointer to an implementation of Persistence_get().
+ */
+ Persistence_get pget;
+ /**
+ * A function pointer to an implementation of Persistence_remove().
+ */
+ Persistence_remove premove;
+ /**
+ * A function pointer to an implementation of Persistence_keys().
+ */
+ Persistence_keys pkeys;
+ /**
+ * A function pointer to an implementation of Persistence_clear().
+ */
+ Persistence_clear pclear;
+ /**
+ * A function pointer to an implementation of Persistence_containskey().
+ */
+ Persistence_containskey pcontainskey;
+} MQTTClient_persistence;
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPacket.c b/tts-mqtt-test/src/paho-mqtt/MQTTPacket.c
new file mode 100644
index 0000000..fb64f6d
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPacket.c
@@ -0,0 +1,737 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - MQTT 3.1.1 support
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief functions to deal with reading and writing of MQTT packets from and to sockets
+ *
+ * Some other related functions are in the MQTTPacketOut module
+ */
+
+#include "../paho-mqtt/MQTTPacket.h"
+
+#include "../paho-mqtt/Log.h"
+#if !defined(NO_PERSISTENCE)
+ #include "../paho-mqtt/MQTTPersistence.h"
+#endif
+#include "../paho-mqtt/Messages.h"
+#include "../paho-mqtt/StackTrace.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../paho-mqtt/Heap.h"
+
+#if !defined(min)
+#define min(A,B) ( (A) < (B) ? (A):(B))
+#endif
+
+/**
+ * List of the predefined MQTT v3 packet names.
+ */
+static const char *packet_names[] =
+{
+ "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
+ "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
+ "PINGREQ", "PINGRESP", "DISCONNECT"
+};
+
+const char** MQTTClient_packet_names = packet_names;
+
+
+/**
+ * Converts an MQTT packet code into its name
+ * @param ptype packet code
+ * @return the corresponding string, or "UNKNOWN"
+ */
+const char* MQTTPacket_name(int ptype)
+{
+ return (ptype >= 0 && ptype <= DISCONNECT) ? packet_names[ptype] : "UNKNOWN";
+}
+
+/**
+ * Array of functions to build packets, indexed according to packet code
+ */
+pf new_packets[] =
+{
+ NULL, /**< reserved */
+ NULL, /**< MQTTPacket_connect*/
+ MQTTPacket_connack, /**< CONNACK */
+ MQTTPacket_publish, /**< PUBLISH */
+ MQTTPacket_ack, /**< PUBACK */
+ MQTTPacket_ack, /**< PUBREC */
+ MQTTPacket_ack, /**< PUBREL */
+ MQTTPacket_ack, /**< PUBCOMP */
+ NULL, /**< MQTTPacket_subscribe*/
+ MQTTPacket_suback, /**< SUBACK */
+ NULL, /**< MQTTPacket_unsubscribe*/
+ MQTTPacket_ack, /**< UNSUBACK */
+ MQTTPacket_header_only, /**< PINGREQ */
+ MQTTPacket_header_only, /**< PINGRESP */
+ MQTTPacket_header_only /**< DISCONNECT */
+};
+
+
+static char* readUTFlen(char** pptr, char* enddata, int* len);
+static int MQTTPacket_send_ack(int type, int msgid, int dup, networkHandles *net);
+
+/**
+ * Reads one MQTT packet from a socket.
+ * @param socket a socket from which to read an MQTT packet
+ * @param error pointer to the error code which is completed if no packet is returned
+ * @return the packet structure or NULL if there was an error
+ */
+void* MQTTPacket_Factory(networkHandles* net, int* error)
+{
+ char* data = NULL;
+ static Header header;
+ size_t remaining_length;
+ int ptype;
+ void* pack = NULL;
+ size_t actual_len = 0;
+
+ FUNC_ENTRY;
+ *error = SOCKET_ERROR; /* indicate whether an error occurred, or not */
+
+ /* read the packet data from the socket */
+#if defined(OPENSSL)
+ *error = (net->ssl) ? SSLSocket_getch(net->ssl, net->socket, &header.byte) : Socket_getch(net->socket, &header.byte);
+#else
+ *error = Socket_getch(net->socket, &header.byte);
+#endif
+ if (*error != TCPSOCKET_COMPLETE) /* first byte is the header byte */
+ goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
+
+ /* now read the remaining length, so we know how much more to read */
+ if ((*error = MQTTPacket_decode(net, &remaining_length)) != TCPSOCKET_COMPLETE)
+ goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
+
+ /* now read the rest, the variable header and payload */
+#if defined(OPENSSL)
+ data = (net->ssl) ? SSLSocket_getdata(net->ssl, net->socket, remaining_length, &actual_len) :
+ Socket_getdata(net->socket, remaining_length, &actual_len);
+#else
+ data = Socket_getdata(net->socket, remaining_length, &actual_len);
+#endif
+ if (data == NULL)
+ {
+ *error = SOCKET_ERROR;
+ goto exit; /* socket error */
+ }
+
+ if (actual_len != remaining_length)
+ *error = TCPSOCKET_INTERRUPTED;
+ else
+ {
+ ptype = header.bits.type;
+ if (ptype < CONNECT || ptype > DISCONNECT || new_packets[ptype] == NULL)
+ Log(TRACE_MIN, 2, NULL, ptype);
+ else
+ {
+ if ((pack = (*new_packets[ptype])(header.byte, data, remaining_length)) == NULL)
+ *error = BAD_MQTT_PACKET;
+#if !defined(NO_PERSISTENCE)
+ else if (header.bits.type == PUBLISH && header.bits.qos == 2)
+ {
+ int buf0len;
+ char *buf = malloc(10);
+ buf[0] = header.byte;
+ buf0len = 1 + MQTTPacket_encode(&buf[1], remaining_length);
+ *error = MQTTPersistence_put(net->socket, buf, buf0len, 1,
+ &data, &remaining_length, header.bits.type, ((Publish *)pack)->msgId, 1);
+ free(buf);
+ }
+#endif
+ }
+ }
+ if (pack)
+ time(&(net->lastReceived));
+exit:
+ FUNC_EXIT_RC(*error);
+ return pack;
+}
+
+
+/**
+ * Sends an MQTT packet in one system call write
+ * @param socket the socket to which to write the data
+ * @param header the one-byte MQTT header
+ * @param buffer the rest of the buffer to write (not including remaining length)
+ * @param buflen the length of the data in buffer to be written
+ * @return the completion code (TCPSOCKET_COMPLETE etc)
+ */
+int MQTTPacket_send(networkHandles* net, Header header, char* buffer, size_t buflen, int freeData)
+{
+ int rc;
+ size_t buf0len;
+ char *buf;
+
+ FUNC_ENTRY;
+ buf = malloc(10);
+ buf[0] = header.byte;
+ buf0len = 1 + MQTTPacket_encode(&buf[1], buflen);
+#if !defined(NO_PERSISTENCE)
+ if (header.bits.type == PUBREL)
+ {
+ char* ptraux = buffer;
+ int msgId = readInt(&ptraux);
+ rc = MQTTPersistence_put(net->socket, buf, buf0len, 1, &buffer, &buflen,
+ header.bits.type, msgId, 0);
+ }
+#endif
+
+#if defined(OPENSSL)
+ if (net->ssl)
+ rc = SSLSocket_putdatas(net->ssl, net->socket, buf, buf0len, 1, &buffer, &buflen, &freeData);
+ else
+#endif
+ rc = Socket_putdatas(net->socket, buf, buf0len, 1, &buffer, &buflen, &freeData);
+
+ if (rc == TCPSOCKET_COMPLETE)
+ time(&(net->lastSent));
+
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(buf);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Sends an MQTT packet from multiple buffers in one system call write
+ * @param socket the socket to which to write the data
+ * @param header the one-byte MQTT header
+ * @param count the number of buffers
+ * @param buffers the rest of the buffers to write (not including remaining length)
+ * @param buflens the lengths of the data in the array of buffers to be written
+ * @return the completion code (TCPSOCKET_COMPLETE etc)
+ */
+int MQTTPacket_sends(networkHandles* net, Header header, int count, char** buffers, size_t* buflens, int* frees)
+{
+ int i, rc;
+ size_t buf0len, total = 0;
+ char *buf;
+
+ FUNC_ENTRY;
+ buf = malloc(10);
+ buf[0] = header.byte;
+ for (i = 0; i < count; i++)
+ total += buflens[i];
+ buf0len = 1 + MQTTPacket_encode(&buf[1], total);
+#if !defined(NO_PERSISTENCE)
+ if (header.bits.type == PUBLISH && header.bits.qos != 0)
+ { /* persist PUBLISH QoS1 and Qo2 */
+ char *ptraux = buffers[2];
+ int msgId = readInt(&ptraux);
+ rc = MQTTPersistence_put(net->socket, buf, buf0len, count, buffers, buflens,
+ header.bits.type, msgId, 0);
+ }
+#endif
+#if defined(OPENSSL)
+ if (net->ssl)
+ rc = SSLSocket_putdatas(net->ssl, net->socket, buf, buf0len, count, buffers, buflens, frees);
+ else
+#endif
+ rc = Socket_putdatas(net->socket, buf, buf0len, count, buffers, buflens, frees);
+
+ if (rc == TCPSOCKET_COMPLETE)
+ time(&(net->lastSent));
+
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(buf);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Encodes the message length according to the MQTT algorithm
+ * @param buf the buffer into which the encoded data is written
+ * @param length the length to be encoded
+ * @return the number of bytes written to buffer
+ */
+int MQTTPacket_encode(char* buf, size_t length)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ do
+ {
+ char d = length % 128;
+ length /= 128;
+ /* if there are more digits to encode, set the top bit of this digit */
+ if (length > 0)
+ d |= 0x80;
+ buf[rc++] = d;
+ } while (length > 0);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Decodes the message length according to the MQTT algorithm
+ * @param socket the socket from which to read the bytes
+ * @param value the decoded length returned
+ * @return the number of bytes read from the socket
+ */
+int MQTTPacket_decode(networkHandles* net, size_t* value)
+{
+ int rc = SOCKET_ERROR;
+ char c;
+ int multiplier = 1;
+ int len = 0;
+#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
+
+ FUNC_ENTRY;
+ *value = 0;
+ do
+ {
+ if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+ {
+ rc = SOCKET_ERROR; /* bad data */
+ goto exit;
+ }
+#if defined(OPENSSL)
+ rc = (net->ssl) ? SSLSocket_getch(net->ssl, net->socket, &c) : Socket_getch(net->socket, &c);
+#else
+ rc = Socket_getch(net->socket, &c);
+#endif
+ if (rc != TCPSOCKET_COMPLETE)
+ goto exit;
+ *value += (c & 127) * multiplier;
+ multiplier *= 128;
+ } while ((c & 128) != 0);
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Calculates an integer from two bytes read from the input buffer
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the integer value calculated
+ */
+int readInt(char** pptr)
+{
+ char* ptr = *pptr;
+ int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
+ *pptr += 2;
+ return len;
+}
+
+
+/**
+ * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means
+ * a length delimited string. So it reads the two byte length then the data according to
+ * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused
+ * by an incorrect length.
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @param enddata pointer to the end of the buffer not to be read beyond
+ * @param len returns the calculcated value of the length bytes read
+ * @return an allocated C string holding the characters read, or NULL if the length read would
+ * have caused an overrun.
+ *
+ */
+static char* readUTFlen(char** pptr, char* enddata, int* len)
+{
+ char* string = NULL;
+
+ FUNC_ENTRY;
+ if (enddata - (*pptr) > 1) /* enough length to read the integer? */
+ {
+ *len = readInt(pptr);
+ if (&(*pptr)[*len] <= enddata)
+ {
+ string = malloc(*len+1);
+ memcpy(string, *pptr, *len);
+ string[*len] = '\0';
+ *pptr += *len;
+ }
+ }
+ FUNC_EXIT;
+ return string;
+}
+
+
+/**
+ * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means
+ * a length delimited string. So it reads the two byte length then the data according to
+ * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused
+ * by an incorrect length.
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @param enddata pointer to the end of the buffer not to be read beyond
+ * @return an allocated C string holding the characters read, or NULL if the length read would
+ * have caused an overrun.
+ */
+char* readUTF(char** pptr, char* enddata)
+{
+ int len;
+ return readUTFlen(pptr, enddata, &len);
+}
+
+
+/**
+ * Reads one character from the input buffer.
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the character read
+ */
+unsigned char readChar(char** pptr)
+{
+ unsigned char c = **pptr;
+ (*pptr)++;
+ return c;
+}
+
+
+/**
+ * Writes one character to an output buffer.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param c the character to write
+ */
+void writeChar(char** pptr, char c)
+{
+ **pptr = c;
+ (*pptr)++;
+}
+
+
+/**
+ * Writes an integer as 2 bytes to an output buffer.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param anInt the integer to write
+ */
+void writeInt(char** pptr, int anInt)
+{
+ **pptr = (char)(anInt / 256);
+ (*pptr)++;
+ **pptr = (char)(anInt % 256);
+ (*pptr)++;
+}
+
+
+/**
+ * Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param string the C string to write
+ */
+void writeUTF(char** pptr, const char* string)
+{
+ size_t len = strlen(string);
+ writeInt(pptr, (int)len);
+ memcpy(*pptr, string, len);
+ *pptr += len;
+}
+
+
+/**
+ * Writes length delimited data to an output buffer
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param data the data to write
+ * @param datalen the length of the data to write
+ */
+void writeData(char** pptr, const void* data, int datalen)
+{
+ writeInt(pptr, datalen);
+ memcpy(*pptr, data, datalen);
+ *pptr += datalen;
+}
+
+
+/**
+ * Function used in the new packets table to create packets which have only a header.
+ * @param aHeader the MQTT header byte
+ * @param data the rest of the packet
+ * @param datalen the length of the rest of the packet
+ * @return pointer to the packet structure
+ */
+void* MQTTPacket_header_only(unsigned char aHeader, char* data, size_t datalen)
+{
+ static unsigned char header = 0;
+ header = aHeader;
+ return &header;
+}
+
+
+/**
+ * Send an MQTT disconnect packet down a socket.
+ * @param socket the open socket to send the data to
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_disconnect(networkHandles *net, const char* clientID)
+{
+ Header header;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ header.byte = 0;
+ header.bits.type = DISCONNECT;
+ rc = MQTTPacket_send(net, header, NULL, 0, 0);
+ Log(LOG_PROTOCOL, 28, NULL, net->socket, clientID, rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Function used in the new packets table to create publish packets.
+ * @param aHeader the MQTT header byte
+ * @param data the rest of the packet
+ * @param datalen the length of the rest of the packet
+ * @return pointer to the packet structure
+ */
+void* MQTTPacket_publish(unsigned char aHeader, char* data, size_t datalen)
+{
+ Publish* pack = malloc(sizeof(Publish));
+ char* curdata = data;
+ char* enddata = &data[datalen];
+
+ FUNC_ENTRY;
+ pack->header.byte = aHeader;
+ if ((pack->topic = readUTFlen(&curdata, enddata, &pack->topiclen)) == NULL) /* Topic name on which to publish */
+ {
+ free(pack);
+ pack = NULL;
+ goto exit;
+ }
+ if (pack->header.bits.qos > 0) /* Msgid only exists for QoS 1 or 2 */
+ pack->msgId = readInt(&curdata);
+ else
+ pack->msgId = 0;
+ pack->payload = curdata;
+ pack->payloadlen = (int)(datalen-(curdata-data));
+exit:
+ FUNC_EXIT;
+ return pack;
+}
+
+
+/**
+ * Free allocated storage for a publish packet.
+ * @param pack pointer to the publish packet structure
+ */
+void MQTTPacket_freePublish(Publish* pack)
+{
+ FUNC_ENTRY;
+ if (pack->topic != NULL)
+ free(pack->topic);
+ free(pack);
+ FUNC_EXIT;
+}
+
+
+/**
+ * Send an MQTT acknowledgement packet down a socket.
+ * @param type the MQTT packet type e.g. SUBACK
+ * @param msgid the MQTT message id to use
+ * @param dup boolean - whether to set the MQTT DUP flag
+ * @param net the network handle to send the data to
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+static int MQTTPacket_send_ack(int type, int msgid, int dup, networkHandles *net)
+{
+ Header header;
+ int rc;
+ char *buf = malloc(2);
+ char *ptr = buf;
+
+ FUNC_ENTRY;
+ header.byte = 0;
+ header.bits.type = type;
+ header.bits.dup = dup;
+ if (type == PUBREL)
+ header.bits.qos = 1;
+ writeInt(&ptr, msgid);
+ if ((rc = MQTTPacket_send(net, header, buf, 2, 1)) != TCPSOCKET_INTERRUPTED)
+ free(buf);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Send an MQTT PUBACK packet down a socket.
+ * @param msgid the MQTT message id to use
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_puback(int msgid, networkHandles* net, const char* clientID)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ rc = MQTTPacket_send_ack(PUBACK, msgid, 0, net);
+ Log(LOG_PROTOCOL, 12, NULL, net->socket, clientID, msgid, rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Free allocated storage for a suback packet.
+ * @param pack pointer to the suback packet structure
+ */
+void MQTTPacket_freeSuback(Suback* pack)
+{
+ FUNC_ENTRY;
+ if (pack->qoss != NULL)
+ ListFree(pack->qoss);
+ free(pack);
+ FUNC_EXIT;
+}
+
+
+/**
+ * Send an MQTT PUBREC packet down a socket.
+ * @param msgid the MQTT message id to use
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_pubrec(int msgid, networkHandles* net, const char* clientID)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ rc = MQTTPacket_send_ack(PUBREC, msgid, 0, net);
+ Log(LOG_PROTOCOL, 13, NULL, net->socket, clientID, msgid, rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Send an MQTT PUBREL packet down a socket.
+ * @param msgid the MQTT message id to use
+ * @param dup boolean - whether to set the MQTT DUP flag
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_pubrel(int msgid, int dup, networkHandles* net, const char* clientID)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ rc = MQTTPacket_send_ack(PUBREL, msgid, dup, net);
+ Log(LOG_PROTOCOL, 16, NULL, net->socket, clientID, msgid, rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Send an MQTT PUBCOMP packet down a socket.
+ * @param msgid the MQTT message id to use
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_pubcomp(int msgid, networkHandles* net, const char* clientID)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ rc = MQTTPacket_send_ack(PUBCOMP, msgid, 0, net);
+ Log(LOG_PROTOCOL, 18, NULL, net->socket, clientID, msgid, rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Function used in the new packets table to create acknowledgement packets.
+ * @param aHeader the MQTT header byte
+ * @param data the rest of the packet
+ * @param datalen the length of the rest of the packet
+ * @return pointer to the packet structure
+ */
+void* MQTTPacket_ack(unsigned char aHeader, char* data, size_t datalen)
+{
+ Ack* pack = malloc(sizeof(Ack));
+ char* curdata = data;
+
+ FUNC_ENTRY;
+ pack->header.byte = aHeader;
+ pack->msgId = readInt(&curdata);
+ FUNC_EXIT;
+ return pack;
+}
+
+
+/**
+ * Send an MQTT PUBLISH packet down a socket.
+ * @param pack a structure from which to get some values to use, e.g topic, payload
+ * @param dup boolean - whether to set the MQTT DUP flag
+ * @param qos the value to use for the MQTT QoS setting
+ * @param retained boolean - whether to set the MQTT retained flag
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, const char* clientID)
+{
+ Header header;
+ char *topiclen;
+ int rc = -1;
+
+ FUNC_ENTRY;
+ topiclen = malloc(2);
+
+ header.bits.type = PUBLISH;
+ header.bits.dup = dup;
+ header.bits.qos = qos;
+ header.bits.retain = retained;
+ if (qos > 0)
+ {
+ char *buf = malloc(2);
+ char *ptr = buf;
+ char* bufs[4] = {topiclen, pack->topic, buf, pack->payload};
+ size_t lens[4] = {2, strlen(pack->topic), 2, pack->payloadlen};
+ int frees[4] = {1, 0, 1, 0};
+
+ writeInt(&ptr, pack->msgId);
+ ptr = topiclen;
+ writeInt(&ptr, (int)lens[1]);
+ rc = MQTTPacket_sends(net, header, 4, bufs, lens, frees);
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(buf);
+ }
+ else
+ {
+ char* ptr = topiclen;
+ char* bufs[3] = {topiclen, pack->topic, pack->payload};
+ size_t lens[3] = {2, strlen(pack->topic), pack->payloadlen};
+ int frees[3] = {1, 0, 0};
+
+ writeInt(&ptr, (int)lens[1]);
+ rc = MQTTPacket_sends(net, header, 3, bufs, lens, frees);
+ }
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(topiclen);
+ if (qos == 0)
+ Log(LOG_PROTOCOL, 27, NULL, net->socket, clientID, retained, rc);
+ else
+ Log(LOG_PROTOCOL, 10, NULL, net->socket, clientID, pack->msgId, qos, retained, rc,
+ min(20, pack->payloadlen), pack->payload);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPacket.h b/tts-mqtt-test/src/paho-mqtt/MQTTPacket.h
new file mode 100644
index 0000000..5a3de49
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPacket.h
@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - MQTT 3.1.1 support
+ * Ian Craggs - big endian Linux reversed definition
+ *******************************************************************************/
+
+#if !defined(MQTTPACKET_H)
+#define MQTTPACKET_H
+
+#include "../paho-mqtt/Socket.h"
+#if defined(OPENSSL)
+#include "../paho-mqtt/SSLSocket.h"
+#endif
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/Clients.h"
+
+/*BE
+include "Socket"
+include "LinkedList"
+include "Clients"
+BE*/
+
+typedef unsigned int bool;
+typedef void* (*pf)(unsigned char, char*, size_t);
+
+#define BAD_MQTT_PACKET -4
+
+enum msgTypes
+{
+ CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
+ PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
+ PINGREQ, PINGRESP, DISCONNECT
+};
+
+#if defined(__linux__)
+#include <endian.h>
+#if __BYTE_ORDER == __BIG_ENDIAN
+ #define REVERSED 1
+#endif
+#endif
+
+/**
+ * Bitfields for the MQTT header byte.
+ */
+typedef union
+{
+ /*unsigned*/ char byte; /**< the whole byte */
+#if defined(REVERSED)
+ struct
+ {
+ unsigned int type : 4; /**< message type nibble */
+ bool dup : 1; /**< DUP flag bit */
+ unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
+ bool retain : 1; /**< retained flag bit */
+ } bits;
+#else
+ struct
+ {
+ bool retain : 1; /**< retained flag bit */
+ unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
+ bool dup : 1; /**< DUP flag bit */
+ unsigned int type : 4; /**< message type nibble */
+ } bits;
+#endif
+} Header;
+
+
+/**
+ * Data for a connect packet.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+ union
+ {
+ unsigned char all; /**< all connect flags */
+#if defined(REVERSED)
+ struct
+ {
+ bool username : 1; /**< 3.1 user name */
+ bool password : 1; /**< 3.1 password */
+ bool willRetain : 1; /**< will retain setting */
+ unsigned int willQoS : 2; /**< will QoS value */
+ bool will : 1; /**< will flag */
+ bool cleanstart : 1; /**< cleansession flag */
+ int : 1; /**< unused */
+ } bits;
+#else
+ struct
+ {
+ int : 1; /**< unused */
+ bool cleanstart : 1; /**< cleansession flag */
+ bool will : 1; /**< will flag */
+ unsigned int willQoS : 2; /**< will QoS value */
+ bool willRetain : 1; /**< will retain setting */
+ bool password : 1; /**< 3.1 password */
+ bool username : 1; /**< 3.1 user name */
+ } bits;
+#endif
+ } flags; /**< connect flags byte */
+
+ char *Protocol, /**< MQTT protocol name */
+ *clientID, /**< string client id */
+ *willTopic, /**< will topic */
+ *willMsg; /**< will payload */
+
+ int keepAliveTimer; /**< keepalive timeout value in seconds */
+ unsigned char version; /**< MQTT version number */
+} Connect;
+
+
+/**
+ * Data for a connack packet.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+ union
+ {
+ unsigned char all; /**< all connack flags */
+#if defined(REVERSED)
+ struct
+ {
+ unsigned int reserved : 7; /**< message type nibble */
+ bool sessionPresent : 1; /**< was a session found on the server? */
+ } bits;
+#else
+ struct
+ {
+ bool sessionPresent : 1; /**< was a session found on the server? */
+ unsigned int reserved : 7; /**< message type nibble */
+ } bits;
+#endif
+ } flags; /**< connack flags byte */
+ char rc; /**< connack return code */
+} Connack;
+
+
+/**
+ * Data for a packet with header only.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+} MQTTPacket;
+
+
+/**
+ * Data for a subscribe packet.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+ int msgId; /**< MQTT message id */
+ List* topics; /**< list of topic strings */
+ List* qoss; /**< list of corresponding QoSs */
+ int noTopics; /**< topic and qos count */
+} Subscribe;
+
+
+/**
+ * Data for a suback packet.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+ int msgId; /**< MQTT message id */
+ List* qoss; /**< list of granted QoSs */
+} Suback;
+
+
+/**
+ * Data for an unsubscribe packet.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+ int msgId; /**< MQTT message id */
+ List* topics; /**< list of topic strings */
+ int noTopics; /**< topic count */
+} Unsubscribe;
+
+
+/**
+ * Data for a publish packet.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+ char* topic; /**< topic string */
+ int topiclen;
+ int msgId; /**< MQTT message id */
+ char* payload; /**< binary payload, length delimited */
+ int payloadlen; /**< payload length */
+} Publish;
+
+
+/**
+ * Data for one of the ack packets.
+ */
+typedef struct
+{
+ Header header; /**< MQTT header byte */
+ int msgId; /**< MQTT message id */
+} Ack;
+
+typedef Ack Puback;
+typedef Ack Pubrec;
+typedef Ack Pubrel;
+typedef Ack Pubcomp;
+typedef Ack Unsuback;
+
+int MQTTPacket_encode(char* buf, size_t length);
+int MQTTPacket_decode(networkHandles* net, size_t* value);
+int readInt(char** pptr);
+char* readUTF(char** pptr, char* enddata);
+unsigned char readChar(char** pptr);
+void writeChar(char** pptr, char c);
+void writeInt(char** pptr, int anInt);
+void writeUTF(char** pptr, const char* string);
+void writeData(char** pptr, const void* data, int datalen);
+
+const char* MQTTPacket_name(int ptype);
+
+void* MQTTPacket_Factory(networkHandles* net, int* error);
+int MQTTPacket_send(networkHandles* net, Header header, char* buffer, size_t buflen, int free);
+int MQTTPacket_sends(networkHandles* net, Header header, int count, char** buffers, size_t* buflens, int* frees);
+
+void* MQTTPacket_header_only(unsigned char aHeader, char* data, size_t datalen);
+int MQTTPacket_send_disconnect(networkHandles* net, const char* clientID);
+
+void* MQTTPacket_publish(unsigned char aHeader, char* data, size_t datalen);
+void MQTTPacket_freePublish(Publish* pack);
+int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, const char* clientID);
+int MQTTPacket_send_puback(int msgid, networkHandles* net, const char* clientID);
+void* MQTTPacket_ack(unsigned char aHeader, char* data, size_t datalen);
+
+void MQTTPacket_freeSuback(Suback* pack);
+int MQTTPacket_send_pubrec(int msgid, networkHandles* net, const char* clientID);
+int MQTTPacket_send_pubrel(int msgid, int dup, networkHandles* net, const char* clientID);
+int MQTTPacket_send_pubcomp(int msgid, networkHandles* net, const char* clientID);
+
+#if !defined(NO_BRIDGE)
+ #include "../paho-mqtt/MQTTPacketOut.h"
+#endif
+
+#endif /* MQTTPACKET_H */
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.c b/tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.c
new file mode 100644
index 0000000..f2c8d29
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.c
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - MQTT 3.1.1 support
+ * Rong Xiang, Ian Craggs - C++ compatibility
+ * Ian Craggs - binary password and will payload
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief functions to deal with reading and writing of MQTT packets from and to sockets
+ *
+ * Some other related functions are in the MQTTPacket module
+ */
+
+
+#include "../paho-mqtt/MQTTPacketOut.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "../paho-mqtt/Heap.h"
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/StackTrace.h"
+
+
+/**
+ * Send an MQTT CONNECT packet down a socket.
+ * @param client a structure from which to get all the required values
+ * @param MQTTVersion the MQTT version to connect with
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_connect(Clients* client, int MQTTVersion)
+{
+ char *buf, *ptr;
+ Connect packet;
+ int rc = -1, len;
+
+ FUNC_ENTRY;
+ packet.header.byte = 0;
+ packet.header.bits.type = CONNECT;
+
+ len = ((MQTTVersion == 3) ? 12 : 10) + (int)strlen(client->clientID)+2;
+ if (client->will)
+ len += (int)strlen(client->will->topic)+2 + client->will->payloadlen+2;
+ if (client->username)
+ len += (int)strlen(client->username)+2;
+ if (client->password)
+ len += client->passwordlen+2;
+
+ ptr = buf = malloc(len);
+ if (MQTTVersion == 3)
+ {
+ writeUTF(&ptr, "MQIsdp");
+ writeChar(&ptr, (char)3);
+ }
+ else if (MQTTVersion == 4)
+ {
+ writeUTF(&ptr, "MQTT");
+ writeChar(&ptr, (char)4);
+ }
+ else
+ goto exit;
+
+ packet.flags.all = 0;
+ packet.flags.bits.cleanstart = client->cleansession;
+ packet.flags.bits.will = (client->will) ? 1 : 0;
+ if (packet.flags.bits.will)
+ {
+ packet.flags.bits.willQoS = client->will->qos;
+ packet.flags.bits.willRetain = client->will->retained;
+ }
+
+ if (client->username)
+ packet.flags.bits.username = 1;
+ if (client->password)
+ packet.flags.bits.password = 1;
+
+ writeChar(&ptr, packet.flags.all);
+ writeInt(&ptr, client->keepAliveInterval);
+ writeUTF(&ptr, client->clientID);
+ if (client->will)
+ {
+ writeUTF(&ptr, client->will->topic);
+ writeData(&ptr, client->will->payload, client->will->payloadlen);
+ }
+ if (client->username)
+ writeUTF(&ptr, client->username);
+ if (client->password)
+ writeData(&ptr, client->password, client->passwordlen);
+
+ rc = MQTTPacket_send(&client->net, packet.header, buf, len, 1);
+ Log(LOG_PROTOCOL, 0, NULL, client->net.socket, client->clientID, client->cleansession, rc);
+exit:
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(buf);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Function used in the new packets table to create connack packets.
+ * @param aHeader the MQTT header byte
+ * @param data the rest of the packet
+ * @param datalen the length of the rest of the packet
+ * @return pointer to the packet structure
+ */
+void* MQTTPacket_connack(unsigned char aHeader, char* data, size_t datalen)
+{
+ Connack* pack = malloc(sizeof(Connack));
+ char* curdata = data;
+
+ FUNC_ENTRY;
+ pack->header.byte = aHeader;
+ pack->flags.all = readChar(&curdata);
+ pack->rc = readChar(&curdata);
+ FUNC_EXIT;
+ return pack;
+}
+
+
+/**
+ * Send an MQTT PINGREQ packet down a socket.
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID)
+{
+ Header header;
+ int rc = 0;
+ size_t buflen = 0;
+
+ FUNC_ENTRY;
+ header.byte = 0;
+ header.bits.type = PINGREQ;
+ rc = MQTTPacket_send(net, header, NULL, buflen,0);
+ Log(LOG_PROTOCOL, 20, NULL, net->socket, clientID, rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Send an MQTT subscribe packet down a socket.
+ * @param topics list of topics
+ * @param qoss list of corresponding QoSs
+ * @param msgid the MQTT message id to use
+ * @param dup boolean - whether to set the MQTT DUP flag
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, networkHandles* net, const char* clientID)
+{
+ Header header;
+ char *data, *ptr;
+ int rc = -1;
+ ListElement *elem = NULL, *qosElem = NULL;
+ int datalen;
+
+ FUNC_ENTRY;
+ header.bits.type = SUBSCRIBE;
+ header.bits.dup = dup;
+ header.bits.qos = 1;
+ header.bits.retain = 0;
+
+ datalen = 2 + topics->count * 3; /* utf length + char qos == 3 */
+ while (ListNextElement(topics, &elem))
+ datalen += (int)strlen((char*)(elem->content));
+ ptr = data = malloc(datalen);
+
+ writeInt(&ptr, msgid);
+ elem = NULL;
+ while (ListNextElement(topics, &elem))
+ {
+ ListNextElement(qoss, &qosElem);
+ writeUTF(&ptr, (char*)(elem->content));
+ writeChar(&ptr, *(int*)(qosElem->content));
+ }
+ rc = MQTTPacket_send(net, header, data, datalen, 1);
+ Log(LOG_PROTOCOL, 22, NULL, net->socket, clientID, msgid, rc);
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(data);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Function used in the new packets table to create suback packets.
+ * @param aHeader the MQTT header byte
+ * @param data the rest of the packet
+ * @param datalen the length of the rest of the packet
+ * @return pointer to the packet structure
+ */
+void* MQTTPacket_suback(unsigned char aHeader, char* data, size_t datalen)
+{
+ Suback* pack = malloc(sizeof(Suback));
+ char* curdata = data;
+
+ FUNC_ENTRY;
+ pack->header.byte = aHeader;
+ pack->msgId = readInt(&curdata);
+ pack->qoss = ListInitialize();
+ while ((size_t)(curdata - data) < datalen)
+ {
+ int* newint;
+ newint = malloc(sizeof(int));
+ *newint = (int)readChar(&curdata);
+ ListAppend(pack->qoss, newint, sizeof(int));
+ }
+ FUNC_EXIT;
+ return pack;
+}
+
+
+/**
+ * Send an MQTT unsubscribe packet down a socket.
+ * @param topics list of topics
+ * @param msgid the MQTT message id to use
+ * @param dup boolean - whether to set the MQTT DUP flag
+ * @param socket the open socket to send the data to
+ * @param clientID the string client identifier, only used for tracing
+ * @return the completion code (e.g. TCPSOCKET_COMPLETE)
+ */
+int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, networkHandles* net, const char* clientID)
+{
+ Header header;
+ char *data, *ptr;
+ int rc = -1;
+ ListElement *elem = NULL;
+ int datalen;
+
+ FUNC_ENTRY;
+ header.bits.type = UNSUBSCRIBE;
+ header.bits.dup = dup;
+ header.bits.qos = 1;
+ header.bits.retain = 0;
+
+ datalen = 2 + topics->count * 2; /* utf length == 2 */
+ while (ListNextElement(topics, &elem))
+ datalen += (int)strlen((char*)(elem->content));
+ ptr = data = malloc(datalen);
+
+ writeInt(&ptr, msgid);
+ elem = NULL;
+ while (ListNextElement(topics, &elem))
+ writeUTF(&ptr, (char*)(elem->content));
+ rc = MQTTPacket_send(net, header, data, datalen, 1);
+ Log(LOG_PROTOCOL, 25, NULL, net->socket, clientID, msgid, rc);
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(data);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.h b/tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.h
new file mode 100644
index 0000000..77eb03f
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPacketOut.h
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - MQTT 3.1.1 support
+ *******************************************************************************/
+
+#if !defined(MQTTPACKETOUT_H)
+#define MQTTPACKETOUT_H
+
+#include "../paho-mqtt/MQTTPacket.h"
+
+int MQTTPacket_send_connect(Clients* client, int MQTTVersion);
+void* MQTTPacket_connack(unsigned char aHeader, char* data, size_t datalen);
+
+int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID);
+
+int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, networkHandles* net, const char* clientID);
+void* MQTTPacket_suback(unsigned char aHeader, char* data, size_t datalen);
+
+int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, networkHandles* net, const char* clientID);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPersistence.c b/tts-mqtt-test/src/paho-mqtt/MQTTPersistence.c
new file mode 100644
index 0000000..c0bfcbe
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPersistence.c
@@ -0,0 +1,655 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - async client updates
+ * Ian Craggs - fix for bug 432903 - queue persistence
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Functions that apply to persistence operations.
+ *
+ */
+
+#include "../paho-mqtt/MQTTPersistence.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../paho-mqtt/Heap.h"
+#include "../paho-mqtt/MQTTPersistenceDefault.h"
+#include "../paho-mqtt/MQTTProtocolClient.h"
+
+
+static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen);
+static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size);
+
+/**
+ * Creates a ::MQTTClient_persistence structure representing a persistence implementation.
+ * @param persistence the ::MQTTClient_persistence structure.
+ * @param type the type of the persistence implementation. See ::MQTTClient_create.
+ * @param pcontext the context for this persistence implementation. See ::MQTTClient_create.
+ * @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
+ */
+#include "../paho-mqtt/StackTrace.h"
+
+int MQTTPersistence_create(MQTTClient_persistence** persistence, int type, void* pcontext)
+{
+ int rc = 0;
+ MQTTClient_persistence* per = NULL;
+
+ FUNC_ENTRY;
+#if !defined(NO_PERSISTENCE)
+ switch (type)
+ {
+ case MQTTCLIENT_PERSISTENCE_NONE :
+ per = NULL;
+ break;
+ case MQTTCLIENT_PERSISTENCE_DEFAULT :
+ per = malloc(sizeof(MQTTClient_persistence));
+ if ( per != NULL )
+ {
+ if ( pcontext != NULL )
+ {
+ per->context = malloc(strlen(pcontext) + 1);
+ strcpy(per->context, pcontext);
+ }
+ else
+ per->context = "."; /* working directory */
+ /* file system functions */
+ per->popen = pstopen;
+ per->pclose = pstclose;
+ per->pput = pstput;
+ per->pget = pstget;
+ per->premove = pstremove;
+ per->pkeys = pstkeys;
+ per->pclear = pstclear;
+ per->pcontainskey = pstcontainskey;
+ }
+ else
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ break;
+ case MQTTCLIENT_PERSISTENCE_USER :
+ per = (MQTTClient_persistence *)pcontext;
+ if ( per == NULL || (per != NULL && (per->context == NULL || per->pclear == NULL ||
+ per->pclose == NULL || per->pcontainskey == NULL || per->pget == NULL || per->pkeys == NULL ||
+ per->popen == NULL || per->pput == NULL || per->premove == NULL)) )
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ break;
+ default:
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ break;
+ }
+#endif
+
+ *persistence = per;
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Open persistent store and restore any persisted messages.
+ * @param client the client as ::Clients.
+ * @param serverURI the URI of the remote end.
+ * @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
+ */
+int MQTTPersistence_initialize(Clients *c, const char *serverURI)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if ( c->persistence != NULL )
+ {
+ rc = c->persistence->popen(&(c->phandle), c->clientID, serverURI, c->persistence->context);
+ if ( rc == 0 )
+ rc = MQTTPersistence_restore(c);
+ }
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Close persistent store.
+ * @param client the client as ::Clients.
+ * @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
+ */
+int MQTTPersistence_close(Clients *c)
+{
+ int rc =0;
+
+ FUNC_ENTRY;
+ if (c->persistence != NULL)
+ {
+ rc = c->persistence->pclose(c->phandle);
+ c->phandle = NULL;
+#if !defined(NO_PERSISTENCE)
+ if ( c->persistence->popen == pstopen )
+ free(c->persistence);
+#endif
+ c->persistence = NULL;
+ }
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+/**
+ * Clears the persistent store.
+ * @param client the client as ::Clients.
+ * @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
+ */
+int MQTTPersistence_clear(Clients *c)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (c->persistence != NULL)
+ rc = c->persistence->pclear(c->phandle);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Restores the persisted records to the outbound and inbound message queues of the
+ * client.
+ * @param client the client as ::Clients.
+ * @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
+ */
+int MQTTPersistence_restore(Clients *c)
+{
+ int rc = 0;
+ char **msgkeys = NULL,
+ *buffer = NULL;
+ int nkeys, buflen;
+ int i = 0;
+ int msgs_sent = 0;
+ int msgs_rcvd = 0;
+
+ FUNC_ENTRY;
+ if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
+ {
+ while (rc == 0 && i < nkeys)
+ {
+ if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) == 0)
+ {
+ ;
+ }
+ else if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) == 0)
+ {
+ ;
+ }
+ else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0)
+ {
+ MQTTPacket* pack = MQTTPersistence_restorePacket(buffer, buflen);
+ if ( pack != NULL )
+ {
+ if ( strstr(msgkeys[i],PERSISTENCE_PUBLISH_RECEIVED) != NULL )
+ {
+ Publish* publish = (Publish*)pack;
+ Messages* msg = NULL;
+ msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain);
+ msg->nextMessageType = PUBREL;
+ /* order does not matter for persisted received messages */
+ ListAppend(c->inboundMsgs, msg, msg->len);
+ publish->topic = NULL;
+ MQTTPacket_freePublish(publish);
+ msgs_rcvd++;
+ }
+ else if ( strstr(msgkeys[i],PERSISTENCE_PUBLISH_SENT) != NULL )
+ {
+ Publish* publish = (Publish*)pack;
+ Messages* msg = NULL;
+ char *key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ sprintf(key, "%s%d", PERSISTENCE_PUBREL, publish->msgId);
+ msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain);
+ if ( c->persistence->pcontainskey(c->phandle, key) == 0 )
+ /* PUBLISH Qo2 and PUBREL sent */
+ msg->nextMessageType = PUBCOMP;
+ /* else: PUBLISH QoS1, or PUBLISH QoS2 and PUBREL not sent */
+ /* retry at the first opportunity */
+ msg->lastTouch = 0;
+ MQTTPersistence_insertInOrder(c->outboundMsgs, msg, msg->len);
+ publish->topic = NULL;
+ MQTTPacket_freePublish(publish);
+ free(key);
+ msgs_sent++;
+ }
+ else if ( strstr(msgkeys[i],PERSISTENCE_PUBREL) != NULL )
+ {
+ /* orphaned PUBRELs ? */
+ Pubrel* pubrel = (Pubrel*)pack;
+ char *key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ sprintf(key, "%s%d", PERSISTENCE_PUBLISH_SENT, pubrel->msgId);
+ if ( c->persistence->pcontainskey(c->phandle, key) != 0 )
+ rc = c->persistence->premove(c->phandle, msgkeys[i]);
+ free(pubrel);
+ free(key);
+ }
+ }
+ else /* pack == NULL -> bad persisted record */
+ rc = c->persistence->premove(c->phandle, msgkeys[i]);
+ }
+ if (buffer)
+ {
+ free(buffer);
+ buffer = NULL;
+ }
+ if (msgkeys[i])
+ free(msgkeys[i]);
+ i++;
+ }
+ if (msgkeys)
+ free(msgkeys);
+ }
+ Log(TRACE_MINIMUM, -1, "%d sent messages and %d received messages restored for client %s\n",
+ msgs_sent, msgs_rcvd, c->clientID);
+ MQTTPersistence_wrapMsgID(c);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Returns a MQTT packet restored from persisted data.
+ * @param buffer the persisted data.
+ * @param buflen the number of bytes of the data buffer.
+ */
+void* MQTTPersistence_restorePacket(char* buffer, size_t buflen)
+{
+ void* pack = NULL;
+ Header header;
+ int fixed_header_length = 1, ptype, remaining_length = 0;
+ char c;
+ int multiplier = 1;
+ extern pf new_packets[];
+
+ FUNC_ENTRY;
+ header.byte = buffer[0];
+ /* decode the message length according to the MQTT algorithm */
+ do
+ {
+ c = *(++buffer);
+ remaining_length += (c & 127) * multiplier;
+ multiplier *= 128;
+ fixed_header_length++;
+ } while ((c & 128) != 0);
+
+ if ( (fixed_header_length + remaining_length) == buflen )
+ {
+ ptype = header.bits.type;
+ if (ptype >= CONNECT && ptype <= DISCONNECT && new_packets[ptype] != NULL)
+ pack = (*new_packets[ptype])(header.byte, ++buffer, remaining_length);
+ }
+
+ FUNC_EXIT;
+ return pack;
+}
+
+
+/**
+ * Inserts the specified message into the list, maintaining message ID order.
+ * @param list the list to insert the message into.
+ * @param content the message to add.
+ * @param size size of the message.
+ */
+void MQTTPersistence_insertInOrder(List* list, void* content, size_t size)
+{
+ ListElement* index = NULL;
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ while(ListNextElement(list, &current) != NULL && index == NULL)
+ {
+ if ( ((Messages*)content)->msgid < ((Messages*)current->content)->msgid )
+ index = current;
+ }
+
+ ListInsert(list, content, size, index);
+ FUNC_EXIT;
+}
+
+
+/**
+ * Adds a record to the persistent store. This function must not be called for QoS0
+ * messages.
+ * @param socket the socket of the client.
+ * @param buf0 fixed header.
+ * @param buf0len length of the fixed header.
+ * @param count number of buffers representing the variable header and/or the payload.
+ * @param buffers the buffers representing the variable header and/or the payload.
+ * @param buflens length of the buffers representing the variable header and/or the payload.
+ * @param msgId the message ID.
+ * @param scr 0 indicates message in the sending direction; 1 indicates message in the
+ * receiving direction.
+ * @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
+ */
+int MQTTPersistence_put(int socket, char* buf0, size_t buf0len, int count,
+ char** buffers, size_t* buflens, int htype, int msgId, int scr )
+{
+ int rc = 0;
+ extern ClientStates* bstate;
+ int nbufs, i;
+ int* lens = NULL;
+ char** bufs = NULL;
+ char *key;
+ Clients* client = NULL;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &socket, clientSocketCompare)->content);
+ if (client->persistence != NULL)
+ {
+ key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ nbufs = 1 + count;
+ lens = (int *)malloc(nbufs * sizeof(int));
+ bufs = (char **)malloc(nbufs * sizeof(char *));
+ lens[0] = (int)buf0len;
+ bufs[0] = buf0;
+ for (i = 0; i < count; i++)
+ {
+ lens[i+1] = (int)buflens[i];
+ bufs[i+1] = buffers[i];
+ }
+
+ /* key */
+ if ( scr == 0 )
+ { /* sending */
+ if (htype == PUBLISH) /* PUBLISH QoS1 and QoS2*/
+ sprintf(key, "%s%d", PERSISTENCE_PUBLISH_SENT, msgId);
+ if (htype == PUBREL) /* PUBREL */
+ sprintf(key, "%s%d", PERSISTENCE_PUBREL, msgId);
+ }
+ if ( scr == 1 ) /* receiving PUBLISH QoS2 */
+ sprintf(key, "%s%d", PERSISTENCE_PUBLISH_RECEIVED, msgId);
+
+ rc = client->persistence->pput(client->phandle, key, nbufs, bufs, lens);
+
+ free(key);
+ free(lens);
+ free(bufs);
+ }
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Deletes a record from the persistent store.
+ * @param client the client as ::Clients.
+ * @param type the type of the persisted record: #PERSISTENCE_PUBLISH_SENT, #PERSISTENCE_PUBREL
+ * or #PERSISTENCE_PUBLISH_RECEIVED.
+ * @param qos the qos field of the message.
+ * @param msgId the message ID.
+ * @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
+ */
+int MQTTPersistence_remove(Clients* c, char *type, int qos, int msgId)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (c->persistence != NULL)
+ {
+ char *key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ if ( (strcmp(type,PERSISTENCE_PUBLISH_SENT) == 0) && qos == 2 )
+ {
+ sprintf(key, "%s%d", PERSISTENCE_PUBLISH_SENT, msgId) ;
+ rc = c->persistence->premove(c->phandle, key);
+ sprintf(key, "%s%d", PERSISTENCE_PUBREL, msgId) ;
+ rc = c->persistence->premove(c->phandle, key);
+ }
+ else /* PERSISTENCE_PUBLISH_SENT && qos == 1 */
+ { /* or PERSISTENCE_PUBLISH_RECEIVED */
+ sprintf(key, "%s%d", type, msgId) ;
+ rc = c->persistence->premove(c->phandle, key);
+ }
+ free(key);
+ }
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Checks whether the message IDs wrapped by looking for the largest gap between two consecutive
+ * message IDs in the outboundMsgs queue.
+ * @param client the client as ::Clients.
+ */
+void MQTTPersistence_wrapMsgID(Clients *client)
+{
+ ListElement* wrapel = NULL;
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ if ( client->outboundMsgs->count > 0 )
+ {
+ int firstMsgID = ((Messages*)client->outboundMsgs->first->content)->msgid;
+ int lastMsgID = ((Messages*)client->outboundMsgs->last->content)->msgid;
+ int gap = MAX_MSG_ID - lastMsgID + firstMsgID;
+ current = ListNextElement(client->outboundMsgs, &current);
+
+ while(ListNextElement(client->outboundMsgs, &current) != NULL)
+ {
+ int curMsgID = ((Messages*)current->content)->msgid;
+ int curPrevMsgID = ((Messages*)current->prev->content)->msgid;
+ int curgap = curMsgID - curPrevMsgID;
+ if ( curgap > gap )
+ {
+ gap = curgap;
+ wrapel = current;
+ }
+ }
+ }
+
+ if ( wrapel != NULL )
+ {
+ /* put wrapel at the beginning of the queue */
+ client->outboundMsgs->first->prev = client->outboundMsgs->last;
+ client->outboundMsgs->last->next = client->outboundMsgs->first;
+ client->outboundMsgs->first = wrapel;
+ client->outboundMsgs->last = wrapel->prev;
+ client->outboundMsgs->first->prev = NULL;
+ client->outboundMsgs->last->next = NULL;
+ }
+ FUNC_EXIT;
+}
+
+
+#if !defined(NO_PERSISTENCE)
+int MQTTPersistence_unpersistQueueEntry(Clients* client, MQTTPersistence_qEntry* qe)
+{
+ int rc = 0;
+ char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
+
+ FUNC_ENTRY;
+ sprintf(key, "%s%u", PERSISTENCE_QUEUE_KEY, qe->seqno);
+ if ((rc = client->persistence->premove(client->phandle, key)) != 0)
+ Log(LOG_ERROR, 0, "Error %d removing qEntry from persistence", rc);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int MQTTPersistence_persistQueueEntry(Clients* aclient, MQTTPersistence_qEntry* qe)
+{
+ int rc = 0;
+ int nbufs = 8;
+ int bufindex = 0;
+ char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
+ int* lens = NULL;
+ void** bufs = NULL;
+
+ FUNC_ENTRY;
+ lens = (int*)malloc(nbufs * sizeof(int));
+ bufs = malloc(nbufs * sizeof(char *));
+
+ bufs[bufindex] = &qe->msg->payloadlen;
+ lens[bufindex++] = sizeof(qe->msg->payloadlen);
+
+ bufs[bufindex] = qe->msg->payload;
+ lens[bufindex++] = qe->msg->payloadlen;
+
+ bufs[bufindex] = &qe->msg->qos;
+ lens[bufindex++] = sizeof(qe->msg->qos);
+
+ bufs[bufindex] = &qe->msg->retained;
+ lens[bufindex++] = sizeof(qe->msg->retained);
+
+ bufs[bufindex] = &qe->msg->dup;
+ lens[bufindex++] = sizeof(qe->msg->dup);
+
+ bufs[bufindex] = &qe->msg->msgid;
+ lens[bufindex++] = sizeof(qe->msg->msgid);
+
+ bufs[bufindex] = qe->topicName;
+ lens[bufindex++] = (int)strlen(qe->topicName) + 1;
+
+ bufs[bufindex] = &qe->topicLen;
+ lens[bufindex++] = sizeof(qe->topicLen);
+
+ sprintf(key, "%s%d", PERSISTENCE_QUEUE_KEY, ++aclient->qentry_seqno);
+ qe->seqno = aclient->qentry_seqno;
+
+ if ((rc = aclient->persistence->pput(aclient->phandle, key, nbufs, (char**)bufs, lens)) != 0)
+ Log(LOG_ERROR, 0, "Error persisting queue entry, rc %d", rc);
+
+ free(lens);
+ free(bufs);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen)
+{
+ MQTTPersistence_qEntry* qe = NULL;
+ char* ptr = buffer;
+ int data_size;
+
+ FUNC_ENTRY;
+ qe = malloc(sizeof(MQTTPersistence_qEntry));
+ memset(qe, '\0', sizeof(MQTTPersistence_qEntry));
+
+ qe->msg = malloc(sizeof(MQTTPersistence_message));
+ memset(qe->msg, '\0', sizeof(MQTTPersistence_message));
+
+ qe->msg->payloadlen = *(int*)ptr;
+ ptr += sizeof(int);
+
+ data_size = qe->msg->payloadlen;
+ qe->msg->payload = malloc(data_size);
+ memcpy(qe->msg->payload, ptr, data_size);
+ ptr += data_size;
+
+ qe->msg->qos = *(int*)ptr;
+ ptr += sizeof(int);
+
+ qe->msg->retained = *(int*)ptr;
+ ptr += sizeof(int);
+
+ qe->msg->dup = *(int*)ptr;
+ ptr += sizeof(int);
+
+ qe->msg->msgid = *(int*)ptr;
+ ptr += sizeof(int);
+
+ data_size = (int)strlen(ptr) + 1;
+ qe->topicName = malloc(data_size);
+ strcpy(qe->topicName, ptr);
+ ptr += data_size;
+
+ qe->topicLen = *(int*)ptr;
+ ptr += sizeof(int);
+
+ FUNC_EXIT;
+ return qe;
+}
+
+
+static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size)
+{
+ ListElement* index = NULL;
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ while (ListNextElement(list, &current) != NULL && index == NULL)
+ {
+ if (qEntry->seqno < ((MQTTPersistence_qEntry*)current->content)->seqno)
+ index = current;
+ }
+ ListInsert(list, qEntry, size, index);
+ FUNC_EXIT;
+}
+
+
+/**
+ * Restores a queue of messages from persistence to memory
+ * @param c the client as ::Clients - the client object to restore the messages to
+ * @return return code, 0 if successful
+ */
+int MQTTPersistence_restoreMessageQueue(Clients* c)
+{
+ int rc = 0;
+ char **msgkeys;
+ int nkeys;
+ int i = 0;
+ int entries_restored = 0;
+
+ FUNC_ENTRY;
+ if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
+ {
+ while (rc == 0 && i < nkeys)
+ {
+ char *buffer = NULL;
+ int buflen;
+
+ if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) != 0)
+ {
+ ;
+ }
+ else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0)
+ {
+ MQTTPersistence_qEntry* qe = MQTTPersistence_restoreQueueEntry(buffer, buflen);
+
+ if (qe)
+ {
+ qe->seqno = atoi(msgkeys[i]+2);
+ MQTTPersistence_insertInSeqOrder(c->messageQueue, qe, sizeof(MQTTPersistence_qEntry));
+ free(buffer);
+ c->qentry_seqno = max(c->qentry_seqno, qe->seqno);
+ entries_restored++;
+ }
+ }
+ if (msgkeys[i])
+ {
+ free(msgkeys[i]);
+ }
+ i++;
+ }
+ if (msgkeys != NULL)
+ free(msgkeys);
+ }
+ Log(TRACE_MINIMUM, -1, "%d queued messages restored for client %s", entries_restored, c->clientID);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPersistence.h b/tts-mqtt-test/src/paho-mqtt/MQTTPersistence.h
new file mode 100644
index 0000000..7d9a546
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPersistence.h
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - async client updates
+ * Ian Craggs - fix for bug 432903 - queue persistence
+ *******************************************************************************/
+
+#if defined(__cplusplus)
+ extern "C" {
+#endif
+
+#include "../paho-mqtt/Clients.h"
+
+/** Stem of the key for a sent PUBLISH QoS1 or QoS2 */
+#define PERSISTENCE_PUBLISH_SENT "s-"
+/** Stem of the key for a sent PUBREL */
+#define PERSISTENCE_PUBREL "sc-"
+/** Stem of the key for a received PUBLISH QoS2 */
+#define PERSISTENCE_PUBLISH_RECEIVED "r-"
+/** Stem of the key for an async client command */
+#define PERSISTENCE_COMMAND_KEY "c-"
+/** Stem of the key for an async client message queue */
+#define PERSISTENCE_QUEUE_KEY "q-"
+#define PERSISTENCE_MAX_KEY_LENGTH 8
+
+int MQTTPersistence_create(MQTTClient_persistence** per, int type, void* pcontext);
+int MQTTPersistence_initialize(Clients* c, const char* serverURI);
+int MQTTPersistence_close(Clients* c);
+int MQTTPersistence_clear(Clients* c);
+int MQTTPersistence_restore(Clients* c);
+void* MQTTPersistence_restorePacket(char* buffer, size_t buflen);
+void MQTTPersistence_insertInOrder(List* list, void* content, size_t size);
+int MQTTPersistence_put(int socket, char* buf0, size_t buf0len, int count,
+ char** buffers, size_t* buflens, int htype, int msgId, int scr);
+int MQTTPersistence_remove(Clients* c, char* type, int qos, int msgId);
+void MQTTPersistence_wrapMsgID(Clients *c);
+
+typedef struct
+{
+ char struct_id[4];
+ int struct_version;
+ int payloadlen;
+ void* payload;
+ int qos;
+ int retained;
+ int dup;
+ int msgid;
+} MQTTPersistence_message;
+
+typedef struct
+{
+ MQTTPersistence_message* msg;
+ char* topicName;
+ int topicLen;
+ unsigned int seqno; /* only used on restore */
+} MQTTPersistence_qEntry;
+
+int MQTTPersistence_unpersistQueueEntry(Clients* client, MQTTPersistence_qEntry* qe);
+int MQTTPersistence_persistQueueEntry(Clients* aclient, MQTTPersistence_qEntry* qe);
+int MQTTPersistence_restoreMessageQueue(Clients* c);
+#ifdef __cplusplus
+ }
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.c b/tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.c
new file mode 100644
index 0000000..621a77e
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.c
@@ -0,0 +1,852 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - async client updates
+ * Ian Craggs - fix for bug 484496
+ * Ian Craggs - fix for issue 285
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief A file system based persistence implementation.
+ *
+ * A directory is specified when the MQTT client is created. When the persistence is then
+ * opened (see ::Persistence_open), a sub-directory is made beneath the base for this
+ * particular client ID and connection key. This allows one persistence base directory to
+ * be shared by multiple clients.
+ *
+ */
+
+#if !defined(NO_PERSISTENCE)
+
+#include "../paho-mqtt/OsWrapper.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#if defined(WIN32) || defined(WIN64)
+ #include <direct.h>
+ /* Windows doesn't have strtok_r, so remap it to strtok */
+ #define strtok_r( A, B, C ) strtok( A, B )
+ int keysWin32(char *, char ***, int *);
+ int clearWin32(char *);
+ int containskeyWin32(char *, char *);
+#else
+ #include <sys/stat.h>
+ #include <dirent.h>
+ #include <unistd.h>
+ int keysUnix(char *, char ***, int *);
+ int clearUnix(char *);
+ int containskeyUnix(char *, char *);
+#endif
+
+#include "../paho-mqtt/MQTTClientPersistence.h"
+#include "../paho-mqtt/MQTTPersistenceDefault.h"
+#include "../paho-mqtt/StackTrace.h"
+#include "../paho-mqtt/Heap.h"
+
+/** Create persistence directory for the client: context/clientID-serverURI.
+ * See ::Persistence_open
+ */
+
+int pstopen(void **handle, const char* clientID, const char* serverURI, void* context)
+{
+ int rc = 0;
+ char *dataDir = context;
+ char *clientDir;
+ char *pToken = NULL;
+ char *save_ptr = NULL;
+ char *pCrtDirName = NULL;
+ char *pTokDirName = NULL;
+ char *perserverURI = NULL, *ptraux;
+
+ FUNC_ENTRY;
+ /* Note that serverURI=address:port, but ":" not allowed in Windows directories */
+ perserverURI = malloc(strlen(serverURI) + 1);
+ strcpy(perserverURI, serverURI);
+ while ((ptraux = strstr(perserverURI, ":")) != NULL)
+ *ptraux = '-' ;
+
+ /* consider '/' + '-' + '\0' */
+ clientDir = malloc(strlen(dataDir) + strlen(clientID) + strlen(perserverURI) + 3);
+ sprintf(clientDir, "%s/%s-%s", dataDir, clientID, perserverURI);
+
+
+ /* create clientDir directory */
+
+ /* pCrtDirName - holds the directory name we are currently trying to create. */
+ /* This gets built up level by level untipwdl the full path name is created.*/
+ /* pTokDirName - holds the directory name that gets used by strtok. */
+ pCrtDirName = (char*)malloc( strlen(clientDir) + 1 );
+ pTokDirName = (char*)malloc( strlen(clientDir) + 1 );
+ strcpy( pTokDirName, clientDir );
+
+ /* If first character is directory separator, make sure it's in the created directory name #285 */
+ if (*pTokDirName == '/' || *pTokDirName == '\\')
+ {
+ *pCrtDirName = *pTokDirName;
+ pToken = strtok_r( pTokDirName + 1, "\\/", &save_ptr );
+ strcpy( pCrtDirName + 1, pToken );
+ }
+ else
+ {
+ pToken = strtok_r( pTokDirName, "\\/", &save_ptr );
+ strcpy( pCrtDirName, pToken );
+ }
+
+ rc = pstmkdir( pCrtDirName );
+ pToken = strtok_r( NULL, "\\/", &save_ptr );
+ while ( (pToken != NULL) && (rc == 0) )
+ {
+ /* Append the next directory level and try to create it */
+ strcat( pCrtDirName, "/" );
+ strcat( pCrtDirName, pToken );
+ rc = pstmkdir( pCrtDirName );
+ pToken = strtok_r( NULL, "\\/", &save_ptr );
+ }
+
+ *handle = clientDir;
+
+ free(pTokDirName);
+ free(pCrtDirName);
+ free(perserverURI);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+/** Function to create a directory.
+ * Returns 0 on success or if the directory already exists.
+ */
+int pstmkdir( char *pPathname )
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ if ( _mkdir( pPathname ) != 0 )
+ {
+#else
+ /* Create a directory with read, write and execute access for the owner and read access for the group */
+#if !defined(_WRS_KERNEL)
+ if ( mkdir( pPathname, S_IRWXU | S_IRGRP ) != 0 )
+#else
+ if ( mkdir( pPathname ) != 0 )
+#endif /* !defined(_WRS_KERNEL) */
+ {
+#endif
+ if ( errno != EEXIST )
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ }
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+/** Write wire message to the client persistence directory.
+ * See ::Persistence_put
+ */
+int pstput(void* handle, char* key, int bufcount, char* buffers[], int buflens[])
+{
+ int rc = 0;
+ char *clientDir = handle;
+ char *file;
+ FILE *fp;
+ size_t bytesWritten = 0,
+ bytesTotal = 0;
+ int i;
+
+ FUNC_ENTRY;
+ if (clientDir == NULL)
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+ /* consider '/' + '\0' */
+ file = malloc(strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2 );
+ sprintf(file, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION);
+
+ fp = fopen(file, "wb");
+ if ( fp != NULL )
+ {
+ for(i=0; i<bufcount; i++)
+ {
+ bytesTotal += buflens[i];
+ bytesWritten += fwrite(buffers[i], sizeof(char), buflens[i], fp );
+ }
+ fclose(fp);
+ fp = NULL;
+ } else
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+
+ if (bytesWritten != bytesTotal)
+ {
+ pstremove(handle, key);
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ }
+
+ free(file);
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+};
+
+
+/** Retrieve a wire message from the client persistence directory.
+ * See ::Persistence_get
+ */
+int pstget(void* handle, char* key, char** buffer, int* buflen)
+{
+ int rc = 0;
+ FILE *fp;
+ char *clientDir = handle;
+ char *file;
+ char *buf;
+ unsigned long fileLen = 0;
+ unsigned long bytesRead = 0;
+
+ FUNC_ENTRY;
+ if (clientDir == NULL)
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+ /* consider '/' + '\0' */
+ file = malloc(strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2);
+ sprintf(file, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION);
+
+ fp = fopen(file, "rb");
+ if ( fp != NULL )
+ {
+ fseek(fp, 0, SEEK_END);
+ fileLen = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ buf=(char *)malloc(fileLen);
+ bytesRead = (int)fread(buf, sizeof(char), fileLen, fp);
+ *buffer = buf;
+ *buflen = bytesRead;
+ if ( bytesRead != fileLen )
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ fclose(fp);
+ fp = NULL;
+ } else
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+
+ free(file);
+ /* the caller must free buf */
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+/** Delete a persisted message from the client persistence directory.
+ * See ::Persistence_remove
+ */
+int pstremove(void* handle, char* key)
+{
+ int rc = 0;
+ char *clientDir = handle;
+ char *file;
+
+ FUNC_ENTRY;
+ if (clientDir == NULL)
+ {
+ return rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+ /* consider '/' + '\0' */
+ file = malloc(strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2);
+ sprintf(file, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION);
+
+#if defined(WIN32) || defined(WIN64)
+ if ( _unlink(file) != 0 )
+ {
+#else
+ if ( unlink(file) != 0 )
+ {
+#endif
+ if ( errno != ENOENT )
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ }
+
+ free(file);
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/** Delete client persistence directory (if empty).
+ * See ::Persistence_close
+ */
+int pstclose(void* handle)
+{
+ int rc = 0;
+ char *clientDir = handle;
+
+ FUNC_ENTRY;
+ if (clientDir == NULL)
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+#if defined(WIN32) || defined(WIN64)
+ if ( _rmdir(clientDir) != 0 )
+ {
+#else
+ if ( rmdir(clientDir) != 0 )
+ {
+#endif
+ if ( errno != ENOENT && errno != ENOTEMPTY )
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ }
+
+ free(clientDir);
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/** Returns whether if a wire message is persisted in the client persistence directory.
+ * See ::Persistence_containskey
+ */
+int pstcontainskey(void *handle, char *key)
+{
+ int rc = 0;
+ char *clientDir = handle;
+
+ FUNC_ENTRY;
+ if (clientDir == NULL)
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+#if defined(WIN32) || defined(WIN64)
+ rc = containskeyWin32(clientDir, key);
+#else
+ rc = containskeyUnix(clientDir, key);
+#endif
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+#if defined(WIN32) || defined(WIN64)
+int containskeyWin32(char *dirname, char *key)
+{
+ int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
+ int fFinished = 0;
+ char *filekey, *ptraux;
+ char dir[MAX_PATH+1];
+ WIN32_FIND_DATAA FileData;
+ HANDLE hDir;
+
+ FUNC_ENTRY;
+ sprintf(dir, "%s/*", dirname);
+
+ hDir = FindFirstFileA(dir, &FileData);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ while (!fFinished)
+ {
+ if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
+ {
+ filekey = malloc(strlen(FileData.cFileName) + 1);
+ strcpy(filekey, FileData.cFileName);
+ ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
+ if ( ptraux != NULL )
+ *ptraux = '\0' ;
+ if(strcmp(filekey, key) == 0)
+ {
+ notFound = 0;
+ fFinished = 1;
+ }
+ free(filekey);
+ }
+ if (!FindNextFileA(hDir, &FileData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ fFinished = 1;
+ }
+ }
+ FindClose(hDir);
+ }
+
+ FUNC_EXIT_RC(notFound);
+ return notFound;
+}
+#else
+int containskeyUnix(char *dirname, char *key)
+{
+ int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
+ char *filekey, *ptraux;
+ DIR *dp;
+ struct dirent *dir_entry;
+ struct stat stat_info;
+
+ FUNC_ENTRY;
+ if((dp = opendir(dirname)) != NULL)
+ {
+ while((dir_entry = readdir(dp)) != NULL && notFound)
+ {
+ char* filename = malloc(strlen(dirname) + strlen(dir_entry->d_name) + 2);
+ sprintf(filename, "%s/%s", dirname, dir_entry->d_name);
+ lstat(filename, &stat_info);
+ free(filename);
+ if(S_ISREG(stat_info.st_mode))
+ {
+ filekey = malloc(strlen(dir_entry->d_name) + 1);
+ strcpy(filekey, dir_entry->d_name);
+ ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
+ if ( ptraux != NULL )
+ *ptraux = '\0' ;
+ if(strcmp(filekey, key) == 0)
+ notFound = 0;
+ free(filekey);
+ }
+ }
+ closedir(dp);
+ }
+
+ FUNC_EXIT_RC(notFound);
+ return notFound;
+}
+#endif
+
+
+/** Delete all the persisted message in the client persistence directory.
+ * See ::Persistence_clear
+ */
+int pstclear(void *handle)
+{
+ int rc = 0;
+ char *clientDir = handle;
+
+ FUNC_ENTRY;
+ if (clientDir == NULL)
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+#if defined(WIN32) || defined(WIN64)
+ rc = clearWin32(clientDir);
+#else
+ rc = clearUnix(clientDir);
+#endif
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+#if defined(WIN32) || defined(WIN64)
+int clearWin32(char *dirname)
+{
+ int rc = 0;
+ int fFinished = 0;
+ char *file;
+ char dir[MAX_PATH+1];
+ WIN32_FIND_DATAA FileData;
+ HANDLE hDir;
+
+ FUNC_ENTRY;
+ sprintf(dir, "%s/*", dirname);
+
+ hDir = FindFirstFileA(dir, &FileData);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ while (!fFinished)
+ {
+ if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
+ {
+ file = malloc(strlen(dirname) + strlen(FileData.cFileName) + 2);
+ sprintf(file, "%s/%s", dirname, FileData.cFileName);
+ rc = remove(file);
+ free(file);
+ if ( rc != 0 )
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ break;
+ }
+ }
+ if (!FindNextFileA(hDir, &FileData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ fFinished = 1;
+ }
+ }
+ FindClose(hDir);
+ } else
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+#else
+int clearUnix(char *dirname)
+{
+ int rc = 0;
+ DIR *dp;
+ struct dirent *dir_entry;
+ struct stat stat_info;
+
+ FUNC_ENTRY;
+ if((dp = opendir(dirname)) != NULL)
+ {
+ while((dir_entry = readdir(dp)) != NULL && rc == 0)
+ {
+ lstat(dir_entry->d_name, &stat_info);
+ if(S_ISREG(stat_info.st_mode))
+ {
+ if ( remove(dir_entry->d_name) != 0 )
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ }
+ }
+ closedir(dp);
+ } else
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+#endif
+
+
+/** Returns the keys (file names w/o the extension) in the client persistence directory.
+ * See ::Persistence_keys
+ */
+int pstkeys(void *handle, char ***keys, int *nkeys)
+{
+ int rc = 0;
+ char *clientDir = handle;
+
+ FUNC_ENTRY;
+ if (clientDir == NULL)
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+#if defined(WIN32) || defined(WIN64)
+ rc = keysWin32(clientDir, keys, nkeys);
+#else
+ rc = keysUnix(clientDir, keys, nkeys);
+#endif
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+#if defined(WIN32) || defined(WIN64)
+int keysWin32(char *dirname, char ***keys, int *nkeys)
+{
+ int rc = 0;
+ char **fkeys = NULL;
+ int nfkeys = 0;
+ char dir[MAX_PATH+1];
+ WIN32_FIND_DATAA FileData;
+ HANDLE hDir;
+ int fFinished = 0;
+ char *ptraux;
+ int i;
+
+ FUNC_ENTRY;
+ sprintf(dir, "%s/*", dirname);
+
+ /* get number of keys */
+ hDir = FindFirstFileA(dir, &FileData);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ while (!fFinished)
+ {
+ if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
+ nfkeys++;
+ if (!FindNextFileA(hDir, &FileData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ fFinished = 1;
+ }
+ }
+ FindClose(hDir);
+ } else
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+ if (nfkeys != 0 )
+ fkeys = (char **)malloc(nfkeys * sizeof(char *));
+
+ /* copy the keys */
+ hDir = FindFirstFileA(dir, &FileData);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ fFinished = 0;
+ i = 0;
+ while (!fFinished)
+ {
+ if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
+ {
+ fkeys[i] = malloc(strlen(FileData.cFileName) + 1);
+ strcpy(fkeys[i], FileData.cFileName);
+ ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
+ if ( ptraux != NULL )
+ *ptraux = '\0' ;
+ i++;
+ }
+ if (!FindNextFileA(hDir, &FileData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ fFinished = 1;
+ }
+ }
+ FindClose(hDir);
+ } else
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+ *nkeys = nfkeys;
+ *keys = fkeys;
+ /* the caller must free keys */
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+#else
+int keysUnix(char *dirname, char ***keys, int *nkeys)
+{
+ int rc = 0;
+ char **fkeys = NULL;
+ int nfkeys = 0;
+ char *ptraux;
+ int i;
+ DIR *dp;
+ struct dirent *dir_entry;
+ struct stat stat_info;
+
+ FUNC_ENTRY;
+ /* get number of keys */
+ if((dp = opendir(dirname)) != NULL)
+ {
+ while((dir_entry = readdir(dp)) != NULL)
+ {
+ char* temp = malloc(strlen(dirname)+strlen(dir_entry->d_name)+2);
+
+ sprintf(temp, "%s/%s", dirname, dir_entry->d_name);
+ if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
+ nfkeys++;
+ free(temp);
+ }
+ closedir(dp);
+ } else
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+
+ if (nfkeys != 0)
+ {
+ fkeys = (char **)malloc(nfkeys * sizeof(char *));
+
+ /* copy the keys */
+ if((dp = opendir(dirname)) != NULL)
+ {
+ i = 0;
+ while((dir_entry = readdir(dp)) != NULL)
+ {
+ char* temp = malloc(strlen(dirname)+strlen(dir_entry->d_name)+2);
+
+ sprintf(temp, "%s/%s", dirname, dir_entry->d_name);
+ if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
+ {
+ fkeys[i] = malloc(strlen(dir_entry->d_name) + 1);
+ strcpy(fkeys[i], dir_entry->d_name);
+ ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
+ if ( ptraux != NULL )
+ *ptraux = '\0' ;
+ i++;
+ }
+ free(temp);
+ }
+ closedir(dp);
+ } else
+ {
+ rc = MQTTCLIENT_PERSISTENCE_ERROR;
+ goto exit;
+ }
+ }
+
+ *nkeys = nfkeys;
+ *keys = fkeys;
+ /* the caller must free keys */
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+#endif
+
+
+
+#if defined(UNIT_TESTS)
+int main (int argc, char *argv[])
+{
+#define MSTEM "m-"
+#define NMSGS 10
+#define NBUFS 4
+#define NDEL 2
+#define RC !rc ? "(Success)" : "(Failed) "
+
+ int rc;
+ char *handle;
+ char *perdir = ".";
+ const char *clientID = "TheUTClient";
+ const char *serverURI = "127.0.0.1:1883";
+
+ char *stem = MSTEM;
+ int msgId, i;
+ int nm[NDEL] = {5 , 8}; /* msgIds to get and remove */
+
+ char *key;
+ char **keys;
+ int nkeys;
+ char *buffer, *buff;
+ int buflen;
+
+ int nbufs = NBUFS;
+ char *bufs[NBUFS] = {"m0", "mm1", "mmm2" , "mmmm3"}; /* message content */
+ int buflens[NBUFS];
+ for(i=0;i<nbufs;i++)
+ buflens[i]=strlen(bufs[i]);
+
+ /* open */
+ /* printf("Persistence directory : %s\n", perdir); */
+ rc = pstopen((void**)&handle, clientID, serverURI, perdir);
+ printf("%s Persistence directory for client %s : %s\n", RC, clientID, handle);
+
+ /* put */
+ for(msgId=0;msgId<NMSGS;msgId++)
+ {
+ key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ sprintf(key, "%s%d", stem, msgId);
+ rc = pstput(handle, key, nbufs, bufs, buflens);
+ printf("%s Adding message %s\n", RC, key);
+ free(key);
+ }
+
+ /* keys ,ie, list keys added */
+ rc = pstkeys(handle, &keys, &nkeys);
+ printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
+ for(i=0;i<nkeys;i++)
+ printf("%13s\n", keys[i]);
+
+ if (keys !=NULL)
+ free(keys);
+
+ /* containskey */
+ for(i=0;i<NDEL;i++)
+ {
+ key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ sprintf(key, "%s%d", stem, nm[i]);
+ rc = pstcontainskey(handle, key);
+ printf("%s Message %s is persisted ?\n", RC, key);
+ free(key);
+ }
+
+ /* get && remove*/
+ for(i=0;i<NDEL;i++)
+ {
+ key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ sprintf(key, "%s%d", stem, nm[i]);
+ rc = pstget(handle, key, &buffer, &buflen);
+ buff = malloc(buflen+1);
+ memcpy(buff, buffer, buflen);
+ buff[buflen] = '\0';
+ printf("%s Retrieving message %s : %s\n", RC, key, buff);
+ rc = pstremove(handle, key);
+ printf("%s Removing message %s\n", RC, key);
+ free(key);
+ free(buff);
+ free(buffer);
+ }
+
+ /* containskey */
+ for(i=0;i<NDEL;i++)
+ {
+ key = malloc(MESSAGE_FILENAME_LENGTH + 1);
+ sprintf(key, "%s%d", stem, nm[i]);
+ rc = pstcontainskey(handle, key);
+ printf("%s Message %s is persisted ?\n", RC, key);
+ free(key);
+ }
+
+ /* keys ,ie, list keys added */
+ rc = pstkeys(handle, &keys, &nkeys);
+ printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
+ for(i=0;i<nkeys;i++)
+ printf("%13s\n", keys[i]);
+
+ if (keys != NULL)
+ free(keys);
+
+
+ /* close -> it will fail, since client persistence directory is not empty */
+ rc = pstclose(&handle);
+ printf("%s Closing client persistence directory for client %s\n", RC, clientID);
+
+ /* clear */
+ rc = pstclear(handle);
+ printf("%s Deleting all persisted messages in %s\n", RC, handle);
+
+ /* keys ,ie, list keys added */
+ rc = pstkeys(handle, &keys, &nkeys);
+ printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
+ for(i=0;i<nkeys;i++)
+ printf("%13s\n", keys[i]);
+
+ if ( keys != NULL )
+ free(keys);
+
+ /* close */
+ rc = pstclose(&handle);
+ printf("%s Closing client persistence directory for client %s\n", RC, clientID);
+}
+#endif
+
+
+#endif /* NO_PERSISTENCE */
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.h b/tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.h
new file mode 100644
index 0000000..27fedd6
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTPersistenceDefault.h
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+/** 8.3 filesystem */
+#define MESSAGE_FILENAME_LENGTH 8
+/** Extension of the filename */
+#define MESSAGE_FILENAME_EXTENSION ".msg"
+
+/* prototypes of the functions for the default file system persistence */
+int pstopen(void** handle, const char* clientID, const char* serverURI, void* context);
+int pstclose(void* handle);
+int pstput(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
+int pstget(void* handle, char* key, char** buffer, int* buflen);
+int pstremove(void* handle, char* key);
+int pstkeys(void* handle, char*** keys, int* nkeys);
+int pstclear(void* handle);
+int pstcontainskey(void* handle, char* key);
+
+int pstmkdir(char *pPathname);
+
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTProtocol.h b/tts-mqtt-test/src/paho-mqtt/MQTTProtocol.h
new file mode 100644
index 0000000..cf5cdb3
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTProtocol.h
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs - MQTT 3.1.1 updates
+ *******************************************************************************/
+
+#if !defined(MQTTPROTOCOL_H)
+#define MQTTPROTOCOL_H
+
+#include "../paho-mqtt/Clients.h"
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/MQTTPacket.h"
+
+#define MAX_MSG_ID 65535
+#define MAX_CLIENTID_LEN 65535
+
+typedef struct
+{
+ int socket;
+ Publications* p;
+} pending_write;
+
+
+typedef struct
+{
+ List publications;
+ unsigned int msgs_received;
+ unsigned int msgs_sent;
+ List pending_writes; /* for qos 0 writes not complete */
+} MQTTProtocol;
+
+
+#include "../paho-mqtt/MQTTProtocolOut.h"
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.c b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.c
new file mode 100644
index 0000000..ddcd68e
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.c
@@ -0,0 +1,789 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - fix for bug 413429 - connectionLost not called
+ * Ian Craggs - fix for bug 421103 - trying to write to same socket, in retry
+ * Rong Xiang, Ian Craggs - C++ compatibility
+ * Ian Craggs - turn off DUP flag for PUBREL - MQTT 3.1.1
+ * Ian Craggs - ensure that acks are not sent if write is outstanding on socket
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Functions dealing with the MQTT protocol exchanges
+ *
+ * Some other related functions are in the MQTTProtocolOut module
+ * */
+
+
+#include "../paho-mqtt/MQTTProtocolClient.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(NO_PERSISTENCE)
+#include "../paho-mqtt/MQTTPersistence.h"
+#endif
+#include "../paho-mqtt/SocketBuffer.h"
+#include "../paho-mqtt/StackTrace.h"
+#include "../paho-mqtt/Heap.h"
+
+#if !defined(min)
+#define min(A,B) ( (A) < (B) ? (A):(B))
+#endif
+
+extern MQTTProtocol state;
+extern ClientStates* bstate;
+
+
+static void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish);
+static int MQTTProtocol_startPublishCommon(
+ Clients* pubclient,
+ Publish* publish,
+ int qos,
+ int retained);
+static void MQTTProtocol_retries(time_t now, Clients* client, int regardless);
+
+/**
+ * List callback function for comparing Message structures by message id
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+int messageIDCompare(void* a, void* b)
+{
+ Messages* msg = (Messages*)a;
+ return msg->msgid == *(int*)b;
+}
+
+
+/**
+ * Assign a new message id for a client. Make sure it isn't already being used and does
+ * not exceed the maximum.
+ * @param client a client structure
+ * @return the next message id to use, or 0 if none available
+ */
+int MQTTProtocol_assignMsgId(Clients* client)
+{
+ int start_msgid = client->msgID;
+ int msgid = start_msgid;
+
+ FUNC_ENTRY;
+ msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
+ while (ListFindItem(client->outboundMsgs, &msgid, messageIDCompare) != NULL)
+ {
+ msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
+ if (msgid == start_msgid)
+ { /* we've tried them all - none free */
+ msgid = 0;
+ break;
+ }
+ }
+ if (msgid != 0)
+ client->msgID = msgid;
+ FUNC_EXIT_RC(msgid);
+ return msgid;
+}
+
+
+static void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish)
+{
+ int len;
+ pending_write* pw = NULL;
+
+ FUNC_ENTRY;
+ /* store the publication until the write is finished */
+ pw = malloc(sizeof(pending_write));
+ Log(TRACE_MIN, 12, NULL);
+ pw->p = MQTTProtocol_storePublication(publish, &len);
+ pw->socket = pubclient->net.socket;
+ ListAppend(&(state.pending_writes), pw, sizeof(pending_write)+len);
+ /* we don't copy QoS 0 messages unless we have to, so now we have to tell the socket buffer where
+ the saved copy is */
+ if (SocketBuffer_updateWrite(pw->socket, pw->p->topic, pw->p->payload) == NULL)
+ Log(LOG_SEVERE, 0, "Error updating write");
+ FUNC_EXIT;
+}
+
+
+/**
+ * Utility function to start a new publish exchange.
+ * @param pubclient the client to send the publication to
+ * @param publish the publication data
+ * @param qos the MQTT QoS to use
+ * @param retained boolean - whether to set the MQTT retained flag
+ * @return the completion code
+ */
+static int MQTTProtocol_startPublishCommon(Clients* pubclient, Publish* publish, int qos, int retained)
+{
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ rc = MQTTPacket_send_publish(publish, 0, qos, retained, &pubclient->net, pubclient->clientID);
+ if (qos == 0 && rc == TCPSOCKET_INTERRUPTED)
+ MQTTProtocol_storeQoS0(pubclient, publish);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Start a new publish exchange. Store any state necessary and try to send the packet
+ * @param pubclient the client to send the publication to
+ * @param publish the publication data
+ * @param qos the MQTT QoS to use
+ * @param retained boolean - whether to set the MQTT retained flag
+ * @param mm - pointer to the message to send
+ * @return the completion code
+ */
+int MQTTProtocol_startPublish(Clients* pubclient, Publish* publish, int qos, int retained, Messages** mm)
+{
+ Publish p = *publish;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (qos > 0)
+ {
+ *mm = MQTTProtocol_createMessage(publish, mm, qos, retained);
+ ListAppend(pubclient->outboundMsgs, *mm, (*mm)->len);
+ /* we change these pointers to the saved message location just in case the packet could not be written
+ entirely; the socket buffer will use these locations to finish writing the packet */
+ p.payload = (*mm)->publish->payload;
+ p.topic = (*mm)->publish->topic;
+ }
+ rc = MQTTProtocol_startPublishCommon(pubclient, &p, qos, retained);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Copy and store message data for retries
+ * @param publish the publication data
+ * @param mm - pointer to the message data to store
+ * @param qos the MQTT QoS to use
+ * @param retained boolean - whether to set the MQTT retained flag
+ * @return pointer to the message data stored
+ */
+Messages* MQTTProtocol_createMessage(Publish* publish, Messages **mm, int qos, int retained)
+{
+ Messages* m = malloc(sizeof(Messages));
+
+ FUNC_ENTRY;
+ m->len = sizeof(Messages);
+ if (*mm == NULL || (*mm)->publish == NULL)
+ {
+ int len1;
+ *mm = m;
+ m->publish = MQTTProtocol_storePublication(publish, &len1);
+ m->len += len1;
+ }
+ else
+ {
+ ++(((*mm)->publish)->refcount);
+ m->publish = (*mm)->publish;
+ }
+ m->msgid = publish->msgId;
+ m->qos = qos;
+ m->retain = retained;
+ time(&(m->lastTouch));
+ if (qos == 2)
+ m->nextMessageType = PUBREC;
+ FUNC_EXIT;
+ return m;
+}
+
+
+/**
+ * Store message data for possible retry
+ * @param publish the publication data
+ * @param len returned length of the data stored
+ * @return the publication stored
+ */
+Publications* MQTTProtocol_storePublication(Publish* publish, int* len)
+{
+ Publications* p = malloc(sizeof(Publications));
+
+ FUNC_ENTRY;
+ p->refcount = 1;
+
+ *len = (int)strlen(publish->topic)+1;
+ if (Heap_findItem(publish->topic))
+ p->topic = publish->topic;
+ else
+ {
+ p->topic = malloc(*len);
+ strcpy(p->topic, publish->topic);
+ }
+ *len += sizeof(Publications);
+
+ p->topiclen = publish->topiclen;
+ p->payloadlen = publish->payloadlen;
+ p->payload = malloc(publish->payloadlen);
+ memcpy(p->payload, publish->payload, p->payloadlen);
+ *len += publish->payloadlen;
+
+ ListAppend(&(state.publications), p, *len);
+ FUNC_EXIT;
+ return p;
+}
+
+/**
+ * Remove stored message data. Opposite of storePublication
+ * @param p stored publication to remove
+ */
+void MQTTProtocol_removePublication(Publications* p)
+{
+ FUNC_ENTRY;
+ if (--(p->refcount) == 0)
+ {
+ free(p->payload);
+ free(p->topic);
+ ListRemove(&(state.publications), p);
+ }
+ FUNC_EXIT;
+}
+
+/**
+ * Process an incoming publish packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handlePublishes(void* pack, int sock)
+{
+ Publish* publish = (Publish*)pack;
+ Clients* client = NULL;
+ char* clientid = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ clientid = client->clientID;
+ Log(LOG_PROTOCOL, 11, NULL, sock, clientid, publish->msgId, publish->header.bits.qos,
+ publish->header.bits.retain, min(20, publish->payloadlen), publish->payload);
+
+ if (publish->header.bits.qos == 0)
+ Protocol_processPublication(publish, client);
+ else if (!Socket_noPendingWrites(sock))
+ rc = SOCKET_ERROR; /* queue acks? */
+ else if (publish->header.bits.qos == 1)
+ {
+ /* send puback before processing the publications because a lot of return publications could fill up the socket buffer */
+ rc = MQTTPacket_send_puback(publish->msgId, &client->net, client->clientID);
+ /* if we get a socket error from sending the puback, should we ignore the publication? */
+ Protocol_processPublication(publish, client);
+ }
+ else if (publish->header.bits.qos == 2)
+ {
+ /* store publication in inbound list */
+ int len;
+ ListElement* listElem = NULL;
+ Messages* m = malloc(sizeof(Messages));
+ Publications* p = MQTTProtocol_storePublication(publish, &len);
+ m->publish = p;
+ m->msgid = publish->msgId;
+ m->qos = publish->header.bits.qos;
+ m->retain = publish->header.bits.retain;
+ m->nextMessageType = PUBREL;
+ if ( ( listElem = ListFindItem(client->inboundMsgs, &(m->msgid), messageIDCompare) ) != NULL )
+ { /* discard queued publication with same msgID that the current incoming message */
+ Messages* msg = (Messages*)(listElem->content);
+ MQTTProtocol_removePublication(msg->publish);
+ ListInsert(client->inboundMsgs, m, sizeof(Messages) + len, listElem);
+ ListRemove(client->inboundMsgs, msg);
+ } else
+ ListAppend(client->inboundMsgs, m, sizeof(Messages) + len);
+ rc = MQTTPacket_send_pubrec(publish->msgId, &client->net, client->clientID);
+ publish->topic = NULL;
+ }
+ MQTTPacket_freePublish(publish);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+/**
+ * Process an incoming puback packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handlePubacks(void* pack, int sock)
+{
+ Puback* puback = (Puback*)pack;
+ Clients* client = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ Log(LOG_PROTOCOL, 14, NULL, sock, client->clientID, puback->msgId);
+
+ /* look for the message by message id in the records of outbound messages for this client */
+ if (ListFindItem(client->outboundMsgs, &(puback->msgId), messageIDCompare) == NULL)
+ Log(TRACE_MIN, 3, NULL, "PUBACK", client->clientID, puback->msgId);
+ else
+ {
+ Messages* m = (Messages*)(client->outboundMsgs->current->content);
+ if (m->qos != 1)
+ Log(TRACE_MIN, 4, NULL, "PUBACK", client->clientID, puback->msgId, m->qos);
+ else
+ {
+ Log(TRACE_MIN, 6, NULL, "PUBACK", client->clientID, puback->msgId);
+ #if !defined(NO_PERSISTENCE)
+ rc = MQTTPersistence_remove(client, PERSISTENCE_PUBLISH_SENT, m->qos, puback->msgId);
+ #endif
+ MQTTProtocol_removePublication(m->publish);
+ ListRemove(client->outboundMsgs, m);
+ }
+ }
+ free(pack);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Process an incoming pubrec packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handlePubrecs(void* pack, int sock)
+{
+ Pubrec* pubrec = (Pubrec*)pack;
+ Clients* client = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ Log(LOG_PROTOCOL, 15, NULL, sock, client->clientID, pubrec->msgId);
+
+ /* look for the message by message id in the records of outbound messages for this client */
+ client->outboundMsgs->current = NULL;
+ if (ListFindItem(client->outboundMsgs, &(pubrec->msgId), messageIDCompare) == NULL)
+ {
+ if (pubrec->header.bits.dup == 0)
+ Log(TRACE_MIN, 3, NULL, "PUBREC", client->clientID, pubrec->msgId);
+ }
+ else
+ {
+ Messages* m = (Messages*)(client->outboundMsgs->current->content);
+ if (m->qos != 2)
+ {
+ if (pubrec->header.bits.dup == 0)
+ Log(TRACE_MIN, 4, NULL, "PUBREC", client->clientID, pubrec->msgId, m->qos);
+ }
+ else if (m->nextMessageType != PUBREC)
+ {
+ if (pubrec->header.bits.dup == 0)
+ Log(TRACE_MIN, 5, NULL, "PUBREC", client->clientID, pubrec->msgId);
+ }
+ else
+ {
+ rc = MQTTPacket_send_pubrel(pubrec->msgId, 0, &client->net, client->clientID);
+ m->nextMessageType = PUBCOMP;
+ time(&(m->lastTouch));
+ }
+ }
+ free(pack);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Process an incoming pubrel packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handlePubrels(void* pack, int sock)
+{
+ Pubrel* pubrel = (Pubrel*)pack;
+ Clients* client = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ Log(LOG_PROTOCOL, 17, NULL, sock, client->clientID, pubrel->msgId);
+
+ /* look for the message by message id in the records of inbound messages for this client */
+ if (ListFindItem(client->inboundMsgs, &(pubrel->msgId), messageIDCompare) == NULL)
+ {
+ if (pubrel->header.bits.dup == 0)
+ Log(TRACE_MIN, 3, NULL, "PUBREL", client->clientID, pubrel->msgId);
+ else if (!Socket_noPendingWrites(sock))
+ rc = SOCKET_ERROR; /* queue acks? */
+ else
+ /* Apparently this is "normal" behaviour, so we don't need to issue a warning */
+ rc = MQTTPacket_send_pubcomp(pubrel->msgId, &client->net, client->clientID);
+ }
+ else
+ {
+ Messages* m = (Messages*)(client->inboundMsgs->current->content);
+ if (m->qos != 2)
+ Log(TRACE_MIN, 4, NULL, "PUBREL", client->clientID, pubrel->msgId, m->qos);
+ else if (m->nextMessageType != PUBREL)
+ Log(TRACE_MIN, 5, NULL, "PUBREL", client->clientID, pubrel->msgId);
+ else if (!Socket_noPendingWrites(sock))
+ rc = SOCKET_ERROR; /* queue acks? */
+ else
+ {
+ Publish publish;
+
+ /* send pubcomp before processing the publications because a lot of return publications could fill up the socket buffer */
+ rc = MQTTPacket_send_pubcomp(pubrel->msgId, &client->net, client->clientID);
+ publish.header.bits.qos = m->qos;
+ publish.header.bits.retain = m->retain;
+ publish.msgId = m->msgid;
+ publish.topic = m->publish->topic;
+ publish.topiclen = m->publish->topiclen;
+ publish.payload = m->publish->payload;
+ publish.payloadlen = m->publish->payloadlen;
+ Protocol_processPublication(&publish, client);
+ #if !defined(NO_PERSISTENCE)
+ rc += MQTTPersistence_remove(client, PERSISTENCE_PUBLISH_RECEIVED, m->qos, pubrel->msgId);
+ #endif
+ ListRemove(&(state.publications), m->publish);
+ ListRemove(client->inboundMsgs, m);
+ ++(state.msgs_received);
+ }
+ }
+ free(pack);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Process an incoming pubcomp packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handlePubcomps(void* pack, int sock)
+{
+ Pubcomp* pubcomp = (Pubcomp*)pack;
+ Clients* client = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ Log(LOG_PROTOCOL, 19, NULL, sock, client->clientID, pubcomp->msgId);
+
+ /* look for the message by message id in the records of outbound messages for this client */
+ if (ListFindItem(client->outboundMsgs, &(pubcomp->msgId), messageIDCompare) == NULL)
+ {
+ if (pubcomp->header.bits.dup == 0)
+ Log(TRACE_MIN, 3, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
+ }
+ else
+ {
+ Messages* m = (Messages*)(client->outboundMsgs->current->content);
+ if (m->qos != 2)
+ Log(TRACE_MIN, 4, NULL, "PUBCOMP", client->clientID, pubcomp->msgId, m->qos);
+ else
+ {
+ if (m->nextMessageType != PUBCOMP)
+ Log(TRACE_MIN, 5, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
+ else
+ {
+ Log(TRACE_MIN, 6, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
+ #if !defined(NO_PERSISTENCE)
+ rc = MQTTPersistence_remove(client, PERSISTENCE_PUBLISH_SENT, m->qos, pubcomp->msgId);
+ #endif
+ MQTTProtocol_removePublication(m->publish);
+ ListRemove(client->outboundMsgs, m);
+ (++state.msgs_sent);
+ }
+ }
+ }
+ free(pack);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * MQTT protocol keepAlive processing. Sends PINGREQ packets as required.
+ * @param now current time
+ */
+void MQTTProtocol_keepalive(time_t now)
+{
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ ListNextElement(bstate->clients, &current);
+ while (current)
+ {
+ Clients* client = (Clients*)(current->content);
+ ListNextElement(bstate->clients, &current);
+ if (client->connected && client->keepAliveInterval > 0 &&
+ (difftime(now, client->net.lastSent) >= client->keepAliveInterval ||
+ difftime(now, client->net.lastReceived) >= client->keepAliveInterval))
+ {
+ if (client->ping_outstanding == 0)
+ {
+ if (Socket_noPendingWrites(client->net.socket))
+ {
+ if (MQTTPacket_send_pingreq(&client->net, client->clientID) != TCPSOCKET_COMPLETE)
+ {
+ Log(TRACE_PROTOCOL, -1, "Error sending PINGREQ for client %s on socket %d, disconnecting", client->clientID, client->net.socket);
+ MQTTProtocol_closeSession(client, 1);
+ }
+ else
+ {
+ client->net.lastSent = now;
+ client->ping_outstanding = 1;
+ }
+ }
+ }
+ else
+ {
+ Log(TRACE_PROTOCOL, -1, "PINGRESP not received in keepalive interval for client %s on socket %d, disconnecting", client->clientID, client->net.socket);
+ MQTTProtocol_closeSession(client, 1);
+ }
+ }
+ }
+ FUNC_EXIT;
+}
+
+
+/**
+ * MQTT retry processing per client
+ * @param now current time
+ * @param client - the client to which to apply the retry processing
+ * @param regardless boolean - retry packets regardless of retry interval (used on reconnect)
+ */
+static void MQTTProtocol_retries(time_t now, Clients* client, int regardless)
+{
+ ListElement* outcurrent = NULL;
+
+ FUNC_ENTRY;
+
+ if (!regardless && client->retryInterval <= 0) /* 0 or -ive retryInterval turns off retry except on reconnect */
+ goto exit;
+
+ while (client && ListNextElement(client->outboundMsgs, &outcurrent) &&
+ client->connected && client->good && /* client is connected and has no errors */
+ Socket_noPendingWrites(client->net.socket)) /* there aren't any previous packets still stacked up on the socket */
+ {
+ Messages* m = (Messages*)(outcurrent->content);
+ if (regardless || difftime(now, m->lastTouch) > max(client->retryInterval, 10))
+ {
+ if (m->qos == 1 || (m->qos == 2 && m->nextMessageType == PUBREC))
+ {
+ Publish publish;
+ int rc;
+
+ Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->net.socket, m->msgid);
+ publish.msgId = m->msgid;
+ publish.topic = m->publish->topic;
+ publish.payload = m->publish->payload;
+ publish.payloadlen = m->publish->payloadlen;
+ rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, &client->net, client->clientID);
+ if (rc == SOCKET_ERROR)
+ {
+ client->good = 0;
+ Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket,
+ Socket_getpeer(client->net.socket));
+ MQTTProtocol_closeSession(client, 1);
+ client = NULL;
+ }
+ else
+ {
+ if (m->qos == 0 && rc == TCPSOCKET_INTERRUPTED)
+ MQTTProtocol_storeQoS0(client, &publish);
+ time(&(m->lastTouch));
+ }
+ }
+ else if (m->qos && m->nextMessageType == PUBCOMP)
+ {
+ Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->net.socket, m->msgid);
+ if (MQTTPacket_send_pubrel(m->msgid, 0, &client->net, client->clientID) != TCPSOCKET_COMPLETE)
+ {
+ client->good = 0;
+ Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket,
+ Socket_getpeer(client->net.socket));
+ MQTTProtocol_closeSession(client, 1);
+ client = NULL;
+ }
+ else
+ time(&(m->lastTouch));
+ }
+ /* break; why not do all retries at once? */
+ }
+ }
+exit:
+ FUNC_EXIT;
+}
+
+
+/**
+ * MQTT retry protocol and socket pending writes processing.
+ * @param now current time
+ * @param doRetry boolean - retries as well as pending writes?
+ * @param regardless boolean - retry packets regardless of retry interval (used on reconnect)
+ */
+void MQTTProtocol_retry(time_t now, int doRetry, int regardless)
+{
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ ListNextElement(bstate->clients, &current);
+ /* look through the outbound message list of each client, checking to see if a retry is necessary */
+ while (current)
+ {
+ Clients* client = (Clients*)(current->content);
+ ListNextElement(bstate->clients, &current);
+ if (client->connected == 0)
+ continue;
+ if (client->good == 0)
+ {
+ MQTTProtocol_closeSession(client, 1);
+ continue;
+ }
+ if (Socket_noPendingWrites(client->net.socket) == 0)
+ continue;
+ if (doRetry)
+ MQTTProtocol_retries(now, client, regardless);
+ }
+ FUNC_EXIT;
+}
+
+
+/**
+ * Free a client structure
+ * @param client the client data to free
+ */
+void MQTTProtocol_freeClient(Clients* client)
+{
+ FUNC_ENTRY;
+ /* free up pending message lists here, and any other allocated data */
+ MQTTProtocol_freeMessageList(client->outboundMsgs);
+ MQTTProtocol_freeMessageList(client->inboundMsgs);
+ ListFree(client->messageQueue);
+ free(client->clientID);
+ client->clientID = NULL;
+ if (client->will)
+ {
+ free(client->will->payload);
+ free(client->will->topic);
+ free(client->will);
+ client->will = NULL;
+ }
+ if (client->username)
+ free((void*)client->username);
+ if (client->password)
+ free((void*)client->password);
+#if defined(OPENSSL)
+ if (client->sslopts)
+ {
+ if (client->sslopts->trustStore)
+ free((void*)client->sslopts->trustStore);
+ if (client->sslopts->keyStore)
+ free((void*)client->sslopts->keyStore);
+ if (client->sslopts->privateKey)
+ free((void*)client->sslopts->privateKey);
+ if (client->sslopts->privateKeyPassword)
+ free((void*)client->sslopts->privateKeyPassword);
+ if (client->sslopts->enabledCipherSuites)
+ free((void*)client->sslopts->enabledCipherSuites);
+ if (client->sslopts->struct_version >= 2)
+ {
+ if (client->sslopts->CApath)
+ free((void*)client->sslopts->CApath);
+ }
+ free(client->sslopts);
+ client->sslopts = NULL;
+ }
+#endif
+ /* don't free the client structure itself... this is done elsewhere */
+ FUNC_EXIT;
+}
+
+
+/**
+ * Empty a message list, leaving it able to accept new messages
+ * @param msgList the message list to empty
+ */
+void MQTTProtocol_emptyMessageList(List* msgList)
+{
+ ListElement* current = NULL;
+
+ FUNC_ENTRY;
+ while (ListNextElement(msgList, &current))
+ {
+ Messages* m = (Messages*)(current->content);
+ MQTTProtocol_removePublication(m->publish);
+ }
+ ListEmpty(msgList);
+ FUNC_EXIT;
+}
+
+
+/**
+ * Empty and free up all storage used by a message list
+ * @param msgList the message list to empty and free
+ */
+void MQTTProtocol_freeMessageList(List* msgList)
+{
+ FUNC_ENTRY;
+ MQTTProtocol_emptyMessageList(msgList);
+ ListFree(msgList);
+ FUNC_EXIT;
+}
+
+
+/**
+* Copy no more than dest_size -1 characters from the string pointed to by src to the array pointed to by dest.
+* The destination string will always be null-terminated.
+* @param dest the array which characters copy to
+* @param src the source string which characters copy from
+* @param dest_size the size of the memory pointed to by dest: copy no more than this -1 (allow for null). Must be >= 1
+* @return the destination string pointer
+*/
+char* MQTTStrncpy(char *dest, const char *src, size_t dest_size)
+{
+ size_t count = dest_size;
+ char *temp = dest;
+
+ FUNC_ENTRY;
+ if (dest_size < strlen(src))
+ Log(TRACE_MIN, -1, "the src string is truncated");
+
+ /* We must copy only the first (dest_size - 1) bytes */
+ while (count > 1 && (*temp++ = *src++))
+ count--;
+
+ *temp = '\0';
+
+ FUNC_EXIT;
+ return dest;
+}
+
+
+/**
+* Duplicate a string, safely, allocating space on the heap
+* @param src the source string which characters copy from
+* @return the duplicated, allocated string
+*/
+char* MQTTStrdup(const char* src)
+{
+ size_t mlen = strlen(src) + 1;
+ char* temp = malloc(mlen);
+ MQTTStrncpy(temp, src, mlen);
+ return temp;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.h b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.h
new file mode 100644
index 0000000..6af9e77
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolClient.h
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - MQTT 3.1.1 updates
+ * Rong Xiang, Ian Craggs - C++ compatibility
+ * Ian Craggs - add debug definition of MQTTStrdup for when needed
+ *******************************************************************************/
+
+#if !defined(MQTTPROTOCOLCLIENT_H)
+#define MQTTPROTOCOLCLIENT_H
+
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/Messages.h"
+#include "../paho-mqtt/MQTTPacket.h"
+#include "../paho-mqtt/MQTTProtocol.h"
+
+#define MAX_MSG_ID 65535
+#define MAX_CLIENTID_LEN 65535
+
+int MQTTProtocol_startPublish(Clients* pubclient, Publish* publish, int qos, int retained, Messages** m);
+Messages* MQTTProtocol_createMessage(Publish* publish, Messages** mm, int qos, int retained);
+Publications* MQTTProtocol_storePublication(Publish* publish, int* len);
+int messageIDCompare(void* a, void* b);
+int MQTTProtocol_assignMsgId(Clients* client);
+void MQTTProtocol_removePublication(Publications* p);
+void Protocol_processPublication(Publish* publish, Clients* client);
+
+int MQTTProtocol_handlePublishes(void* pack, int sock);
+int MQTTProtocol_handlePubacks(void* pack, int sock);
+int MQTTProtocol_handlePubrecs(void* pack, int sock);
+int MQTTProtocol_handlePubrels(void* pack, int sock);
+int MQTTProtocol_handlePubcomps(void* pack, int sock);
+
+void MQTTProtocol_closeSession(Clients* c, int sendwill);
+void MQTTProtocol_keepalive(time_t);
+void MQTTProtocol_retry(time_t, int, int);
+void MQTTProtocol_freeClient(Clients* client);
+void MQTTProtocol_emptyMessageList(List* msgList);
+void MQTTProtocol_freeMessageList(List* msgList);
+
+char* MQTTStrncpy(char *dest, const char* src, size_t num);
+char* MQTTStrdup(const char* src);
+
+//#define MQTTStrdup(src) MQTTStrncpy(malloc(strlen(src)+1), src, strlen(src)+1)
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.c b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.c
new file mode 100644
index 0000000..d15b707
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.c
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - fix for buffer overflow in addressPort bug #433290
+ * Ian Craggs - MQTT 3.1.1 support
+ * Rong Xiang, Ian Craggs - C++ compatibility
+ * Ian Craggs - fix for bug 479376
+ * Ian Craggs - SNI support
+ * Ian Craggs - fix for issue #164
+ * Ian Craggs - fix for issue #179
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Functions dealing with the MQTT protocol exchanges
+ *
+ * Some other related functions are in the MQTTProtocolClient module
+ */
+
+#include "../paho-mqtt/MQTTProtocolOut.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../paho-mqtt/Heap.h"
+#include "../paho-mqtt/StackTrace.h"
+
+extern ClientStates* bstate;
+
+
+
+/**
+ * Separates an address:port into two separate values
+ * @param uri the input string - hostname:port
+ * @param port the returned port integer
+ * @return the address string
+ */
+char* MQTTProtocol_addressPort(const char* uri, int* port)
+{
+ char* colon_pos = strrchr(uri, ':'); /* reverse find to allow for ':' in IPv6 addresses */
+ char* buf = (char*)uri;
+ size_t len;
+
+ FUNC_ENTRY;
+ if (uri[0] == '[')
+ { /* ip v6 */
+ if (colon_pos < strrchr(uri, ']'))
+ colon_pos = NULL; /* means it was an IPv6 separator, not for host:port */
+ }
+
+ if (colon_pos) /* have to strip off the port */
+ {
+ size_t addr_len = colon_pos - uri;
+ buf = malloc(addr_len + 1);
+ *port = atoi(colon_pos + 1);
+ MQTTStrncpy(buf, uri, addr_len+1);
+ }
+ else
+ *port = DEFAULT_PORT;
+
+ len = strlen(buf);
+ if (buf[len - 1] == ']')
+ {
+ if (buf == (char*)uri)
+ {
+ buf = malloc(len); /* we are stripping off the final ], so length is 1 shorter */
+ MQTTStrncpy(buf, uri, len);
+ }
+ else
+ buf[len - 1] = '\0';
+ }
+ FUNC_EXIT;
+ return buf;
+}
+
+
+/**
+ * MQTT outgoing connect processing for a client
+ * @param ip_address the TCP address:port to connect to
+ * @param aClient a structure with all MQTT data needed
+ * @param int ssl
+ * @param int MQTTVersion the MQTT version to connect with (3 or 4)
+ * @return return code
+ */
+#if defined(OPENSSL)
+int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int ssl, int MQTTVersion)
+#else
+int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int MQTTVersion)
+#endif
+{
+ int rc, port;
+ char* addr;
+
+ FUNC_ENTRY;
+ aClient->good = 1;
+
+ addr = MQTTProtocol_addressPort(ip_address, &port);
+ rc = Socket_new(addr, port, &(aClient->net.socket));
+ if (rc == EINPROGRESS || rc == EWOULDBLOCK)
+ aClient->connect_state = 1; /* TCP connect called - wait for connect completion */
+ else if (rc == 0)
+ { /* TCP connect completed. If SSL, send SSL connect */
+#if defined(OPENSSL)
+ if (ssl)
+ {
+ if (SSLSocket_setSocketForSSL(&aClient->net, aClient->sslopts, addr) == 1)
+ {
+ rc = SSLSocket_connect(aClient->net.ssl, aClient->net.socket,
+ addr, aClient->sslopts->verify);
+ if (rc == TCPSOCKET_INTERRUPTED)
+ aClient->connect_state = 2; /* SSL connect called - wait for completion */
+ }
+ else
+ rc = SOCKET_ERROR;
+ }
+#endif
+
+ if (rc == 0)
+ {
+ /* Now send the MQTT connect packet */
+ if ((rc = MQTTPacket_send_connect(aClient, MQTTVersion)) == 0)
+ aClient->connect_state = 3; /* MQTT Connect sent - wait for CONNACK */
+ else
+ aClient->connect_state = 0;
+ }
+ }
+ if (addr != ip_address)
+ free(addr);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Process an incoming pingresp packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handlePingresps(void* pack, int sock)
+{
+ Clients* client = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ Log(LOG_PROTOCOL, 21, NULL, sock, client->clientID);
+ client->ping_outstanding = 0;
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * MQTT outgoing subscribe processing for a client
+ * @param client the client structure
+ * @param topics list of topics
+ * @param qoss corresponding list of QoSs
+ * @return completion code
+ */
+int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss, int msgID)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ /* we should stack this up for retry processing too */
+ rc = MQTTPacket_send_subscribe(topics, qoss, msgID, 0, &client->net, client->clientID);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Process an incoming suback packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handleSubacks(void* pack, int sock)
+{
+ Suback* suback = (Suback*)pack;
+ Clients* client = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ Log(LOG_PROTOCOL, 23, NULL, sock, client->clientID, suback->msgId);
+ MQTTPacket_freeSuback(suback);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * MQTT outgoing unsubscribe processing for a client
+ * @param client the client structure
+ * @param topics list of topics
+ * @return completion code
+ */
+int MQTTProtocol_unsubscribe(Clients* client, List* topics, int msgID)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ /* we should stack this up for retry processing too? */
+ rc = MQTTPacket_send_unsubscribe(topics, msgID, 0, &client->net, client->clientID);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Process an incoming unsuback packet for a socket
+ * @param pack pointer to the publish packet
+ * @param sock the socket on which the packet was received
+ * @return completion code
+ */
+int MQTTProtocol_handleUnsubacks(void* pack, int sock)
+{
+ Unsuback* unsuback = (Unsuback*)pack;
+ Clients* client = NULL;
+ int rc = TCPSOCKET_COMPLETE;
+
+ FUNC_ENTRY;
+ client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
+ Log(LOG_PROTOCOL, 24, NULL, sock, client->clientID, unsuback->msgId);
+ free(unsuback);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.h b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.h
new file mode 100644
index 0000000..b94fb35
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTProtocolOut.h
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - MQTT 3.1.1 support
+ * Ian Craggs - SNI support
+ *******************************************************************************/
+
+#if !defined(MQTTPROTOCOLOUT_H)
+#define MQTTPROTOCOLOUT_H
+
+#include "../paho-mqtt/Clients.h"
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/Messages.h"
+#include "../paho-mqtt/MQTTPacket.h"
+#include "../paho-mqtt/MQTTProtocol.h"
+#include "../paho-mqtt/MQTTProtocolClient.h"
+
+#define DEFAULT_PORT 1883
+
+char* MQTTProtocol_addressPort(const char* uri, int* port);
+void MQTTProtocol_reconnect(const char* ip_address, Clients* client);
+#if defined(OPENSSL)
+int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int ssl, int MQTTVersion);
+#else
+int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int MQTTVersion);
+#endif
+int MQTTProtocol_handlePingresps(void* pack, int sock);
+int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss, int msgID);
+int MQTTProtocol_handleSubacks(void* pack, int sock);
+int MQTTProtocol_unsubscribe(Clients* client, List* topics, int msgID);
+int MQTTProtocol_handleUnsubacks(void* pack, int sock);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/MQTTVersion.c b/tts-mqtt-test/src/paho-mqtt/MQTTVersion.c
new file mode 100644
index 0000000..7e7bfc1
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/MQTTVersion.c
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2015 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include <stdio.h>
+
+#if !defined(_WRS_KERNEL)
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include "../paho-mqtt/MQTTAsync.h"
+
+#if defined(WIN32) || defined(WIN64)
+#include <windows.h>
+#include <tchar.h>
+#include <io.h>
+#include <sys/stat.h>
+#else
+#include <dlfcn.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
+
+/**
+ *
+ * @file
+ * \brief MQTTVersion - display the version and build information strings for a library.
+ *
+ * With no arguments, we try to load and call the version string for the libraries we
+ * know about: mqttv3c, mqttv3cs, mqttv3a, mqttv3as.
+ * With an argument:
+ * 1) we try to load the named library, call getVersionInfo and display those values.
+ * 2) If that doesn't work, we look through the binary for eyecatchers, and display those.
+ * This will work if the library is not executable in the current environment.
+ *
+ * */
+
+
+ static const char* libraries[] = {"paho-mqtt3c", "paho-mqtt3cs", "paho-mqtt3a", "paho-mqtt3as"};
+ static const char* eyecatchers[] = {"MQTTAsyncV3_Version", "MQTTAsyncV3_Timestamp",
+ "MQTTClientV3_Version", "MQTTClientV3_Timestamp"};
+
+
+char* FindString(char* filename, const char* eyecatcher_input);
+int printVersionInfo(MQTTAsync_nameValue* info);
+int loadandcall(char* libname);
+void printEyecatchers(char* filename);
+
+
+/**
+ * Finds an eyecatcher in a binary file and returns the following value.
+ * @param filename the name of the file
+ * @param eyecatcher_input the eyecatcher string to look for
+ * @return the value found - "" if not found
+ */
+char* FindString(char* filename, const char* eyecatcher_input)
+{
+ FILE* infile = NULL;
+ static char value[100];
+ const char* eyecatcher = eyecatcher_input;
+
+ memset(value, 0, 100);
+ if ((infile = fopen(filename, "rb")) != NULL)
+ {
+ size_t buflen = strlen(eyecatcher);
+ char* buffer = (char*) malloc(buflen);
+
+ if (buffer != NULL)
+ {
+ int c = fgetc(infile);
+
+ while (feof(infile) == 0)
+ {
+ int count = 0;
+ buffer[count++] = c;
+ if (memcmp(eyecatcher, buffer, buflen) == 0)
+ {
+ char* ptr = value;
+ c = fgetc(infile); /* skip space */
+ c = fgetc(infile);
+ while (isprint(c))
+ {
+ *ptr++ = c;
+ c = fgetc(infile);
+ }
+ break;
+ }
+ if (count == buflen)
+ {
+ memmove(buffer, &buffer[1], buflen - 1);
+ count--;
+ }
+ c = fgetc(infile);
+ }
+ free(buffer);
+ }
+
+ fclose(infile);
+ }
+ return value;
+}
+
+
+int printVersionInfo(MQTTAsync_nameValue* info)
+{
+ int rc = 0;
+
+ while (info->name)
+ {
+ printf("%s: %s\n", info->name, info->value);
+ info++;
+ rc = 1; /* at least one value printed */
+ }
+ return rc;
+}
+
+typedef MQTTAsync_nameValue* (*func_type)(void);
+
+int loadandcall(char* libname)
+{
+ int rc = 0;
+ MQTTAsync_nameValue* (*func_address)(void) = NULL;
+#if defined(WIN32) || defined(WIN64)
+ wchar_t wlibname[30];
+ HMODULE APILibrary;
+
+ mbstowcs(wlibname, libname, strlen(libname) + 1);
+ if ((APILibrary = LoadLibrary(wlibname)) == NULL)
+ printf("Error loading library %s, error code %d\n", libname, GetLastError());
+ else
+ {
+ func_address = (func_type)GetProcAddress(APILibrary, "MQTTAsync_getVersionInfo");
+ if (func_address == NULL)
+ func_address = (func_type)GetProcAddress(APILibrary, "MQTTClient_getVersionInfo");
+ if (func_address)
+ rc = printVersionInfo((*func_address)());
+ FreeLibrary(APILibrary);
+ }
+#else
+ void* APILibrary = dlopen(libname, RTLD_LAZY); /* Open the Library in question */
+ char* ErrorOutput = dlerror(); /* Check it opened properly */
+ if (ErrorOutput != NULL)
+ printf("Error loading library %s, error %s\n", libname, ErrorOutput);
+ else
+ {
+ *(void **) (&func_address) = dlsym(APILibrary, "MQTTAsync_getVersionInfo");
+ if (func_address == NULL)
+ func_address = dlsym(APILibrary, "MQTTClient_getVersionInfo");
+ if (func_address)
+ rc = printVersionInfo((*func_address)());
+ dlclose(APILibrary);
+ }
+#endif
+ return rc;
+}
+
+
+#if !defined(ARRAY_SIZE)
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+void printEyecatchers(char* filename)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(eyecatchers); ++i)
+ {
+ char* value = FindString(filename, eyecatchers[i]);
+ if (value[0])
+ printf("%s: %s\n", eyecatchers[i], value);
+ }
+}
+
+
+int main(int argc, char** argv)
+{
+ printf("MQTTVersion: print the version strings of an MQTT client library\n");
+ printf("Copyright (c) 2012, 2015 IBM Corp.\n");
+
+ if (argc == 1)
+ {
+ int i = 0;
+ char namebuf[60];
+
+ printf("Specify a particular library name if it is not in the current directory, or not executable on this platform\n");
+
+ for (i = 0; i < ARRAY_SIZE(libraries); ++i)
+ {
+#if defined(WIN32) || defined(WIN64)
+ sprintf(namebuf, "%s.dll", libraries[i]);
+#else
+ sprintf(namebuf, "lib%s.so.1", libraries[i]);
+#endif
+ printf("--- Trying library %s ---\n", libraries[i]);
+ if (!loadandcall(namebuf))
+ printEyecatchers(namebuf);
+ }
+ }
+ else
+ {
+ if (!loadandcall(argv[1]))
+ printEyecatchers(argv[1]);
+ }
+
+ return 0;
+}
+#else
+int main(void)
+{
+ fprintf(stderr, "This tool is not supported on this platform yet.\n");
+ return 1;
+}
+#endif /* !defined(_WRS_KERNEL) */
diff --git a/tts-mqtt-test/src/paho-mqtt/Messages.c b/tts-mqtt-test/src/paho-mqtt/Messages.c
new file mode 100644
index 0000000..a4174c5
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Messages.c
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Trace messages
+ *
+ */
+
+
+#include "../paho-mqtt/Messages.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../paho-mqtt/Heap.h"
+#include "../paho-mqtt/Log.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+#define max_msg_len 120
+
+static const char *protocol_message_list[] =
+{
+ "%d %s -> CONNECT cleansession: %d (%d)", /* 0, was 131, 68 and 69 */
+ "%d %s <- CONNACK rc: %d", /* 1, was 132 */
+ "%d %s -> CONNACK rc: %d (%d)", /* 2, was 138 */
+ "%d %s <- PINGREQ", /* 3, was 35 */
+ "%d %s -> PINGRESP (%d)", /* 4 */
+ "%d %s <- DISCONNECT", /* 5 */
+ "%d %s <- SUBSCRIBE msgid: %d", /* 6, was 39 */
+ "%d %s -> SUBACK msgid: %d (%d)", /* 7, was 40 */
+ "%d %s <- UNSUBSCRIBE msgid: %d", /* 8, was 41 */
+ "%d %s -> UNSUBACK msgid: %d (%d)", /* 9 */
+ "%d %s -> PUBLISH msgid: %d qos: %d retained: %d (%d) payload: %.*s", /* 10, was 42 */
+ "%d %s <- PUBLISH msgid: %d qos: %d retained: %d payload: %.*s", /* 11, was 46 */
+ "%d %s -> PUBACK msgid: %d (%d)", /* 12, was 47 */
+ "%d %s -> PUBREC msgid: %d (%d)", /* 13, was 48 */
+ "%d %s <- PUBACK msgid: %d", /* 14, was 49 */
+ "%d %s <- PUBREC msgid: %d", /* 15, was 53 */
+ "%d %s -> PUBREL msgid: %d (%d)", /* 16, was 57 */
+ "%d %s <- PUBREL msgid %d", /* 17, was 58 */
+ "%d %s -> PUBCOMP msgid %d (%d)", /* 18, was 62 */
+ "%d %s <- PUBCOMP msgid:%d", /* 19, was 63 */
+ "%d %s -> PINGREQ (%d)", /* 20, was 137 */
+ "%d %s <- PINGRESP", /* 21, was 70 */
+ "%d %s -> SUBSCRIBE msgid: %d (%d)", /* 22, was 72 */
+ "%d %s <- SUBACK msgid: %d", /* 23, was 73 */
+ "%d %s <- UNSUBACK msgid: %d", /* 24, was 74 */
+ "%d %s -> UNSUBSCRIBE msgid: %d (%d)", /* 25, was 106 */
+ "%d %s <- CONNECT", /* 26 */
+ "%d %s -> PUBLISH qos: 0 retained: %d (%d)", /* 27 */
+ "%d %s -> DISCONNECT (%d)", /* 28 */
+ "Socket error for client identifier %s, socket %d, peer address %s; ending connection", /* 29 */
+};
+
+static const char *trace_message_list[] =
+{
+ "Failed to remove client from bstate->clients", /* 0 */
+ "Removed client %s from bstate->clients, socket %d", /* 1 */
+ "Packet_Factory: unhandled packet type %d", /* 2 */
+ "Packet %s received from client %s for message identifier %d, but no record of that message identifier found", /* 3 */
+ "Packet %s received from client %s for message identifier %d, but message is wrong QoS, %d", /* 4 */
+ "Packet %s received from client %s for message identifier %d, but message is in wrong state", /* 5 */
+ "%s received from client %s for message id %d - removing publication", /* 6 */
+ "Trying %s again for client %s, socket %d, message identifier %d", /* 7 */
+ "", /* 8 */
+ "(%lu) %*s(%d)> %s:%d", /* 9 */
+ "(%lu) %*s(%d)< %s:%d", /* 10 */
+ "(%lu) %*s(%d)< %s:%d (%d)", /* 11 */
+ "Storing unsent QoS 0 message", /* 12 */
+};
+
+/**
+ * Get a log message by its index
+ * @param index the integer index
+ * @param log_level the log level, used to determine which message list to use
+ * @return the message format string
+ */
+const char* Messages_get(int index, enum LOG_LEVELS log_level)
+{
+ const char *msg = NULL;
+
+ if (log_level == TRACE_PROTOCOL)
+ msg = (index >= 0 && index < ARRAY_SIZE(protocol_message_list)) ? protocol_message_list[index] : NULL;
+ else
+ msg = (index >= 0 && index < ARRAY_SIZE(trace_message_list)) ? trace_message_list[index] : NULL;
+ return msg;
+}
+
diff --git a/tts-mqtt-test/src/paho-mqtt/Messages.h b/tts-mqtt-test/src/paho-mqtt/Messages.h
new file mode 100644
index 0000000..f6763ce
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Messages.h
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#if !defined(MESSAGES_H)
+#define MESSAGES_H
+
+#include "../paho-mqtt/Log.h"
+
+const char* Messages_get(int, enum LOG_LEVELS);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/OsWrapper.c b/tts-mqtt-test/src/paho-mqtt/OsWrapper.c
new file mode 100644
index 0000000..f83f0c3
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/OsWrapper.c
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 2017 logi.cals GmbH
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Gunter Raidl - timer support for VxWorks
+ * Rainer Poisel - reusability
+ *******************************************************************************/
+
+#include "../paho-mqtt/OsWrapper.h"
+
+#if defined(_WRS_KERNEL)
+void usleep(useconds_t useconds)
+{
+ struct timespec tv;
+ tv.tv_sec = useconds / 1000000;
+ tv.tv_nsec = (useconds % 1000000) * 1000;
+ nanosleep(&tv, NULL);
+}
+#endif /* defined(_WRS_KERNEL) */
diff --git a/tts-mqtt-test/src/paho-mqtt/OsWrapper.h b/tts-mqtt-test/src/paho-mqtt/OsWrapper.h
new file mode 100644
index 0000000..f657ab1
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/OsWrapper.h
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 2017 logi.cals GmbH
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Gunter Raidl - timer support for VxWorks
+ * Rainer Poisel - reusability
+ *******************************************************************************/
+
+#if !defined(OSWRAPPER_H)
+#define OSWRAPPER_H
+
+#if defined(_WRS_KERNEL)
+#include <time.h>
+
+#define lstat stat
+
+typedef unsigned long useconds_t;
+void usleep(useconds_t useconds);
+
+#define timersub(a, b, result) \
+ do \
+ { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) \
+ { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000L; \
+ } \
+ } while (0)
+#endif /* defined(_WRS_KERNEL) */
+
+#endif /* OSWRAPPER_H */
diff --git a/tts-mqtt-test/src/paho-mqtt/SSLSocket.c b/tts-mqtt-test/src/paho-mqtt/SSLSocket.c
new file mode 100644
index 0000000..0dadfef
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/SSLSocket.c
@@ -0,0 +1,947 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs, Allan Stockdill-Mander - initial implementation
+ * Ian Craggs - fix for bug #409702
+ * Ian Craggs - allow compilation for OpenSSL < 1.0
+ * Ian Craggs - fix for bug #453883
+ * Ian Craggs - fix for bug #480363, issue 13
+ * Ian Craggs - SNI support
+ * Ian Craggs - fix for issues #155, #160
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief SSL related functions
+ *
+ */
+
+#if defined(OPENSSL)
+
+#include "SocketBuffer.h"
+#include "MQTTClient.h"
+#include "SSLSocket.h"
+#include "Log.h"
+#include "StackTrace.h"
+#include "Socket.h"
+char* MQTTProtocol_addressPort(const char* uri, int* port);
+
+#include "Heap.h"
+
+#include <string.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h>
+#include <openssl/x509v3.h>
+
+extern Sockets s;
+
+int SSLSocket_error(char* aString, SSL* ssl, int sock, int rc);
+char* SSL_get_verify_result_string(int rc);
+void SSL_CTX_info_callback(const SSL* ssl, int where, int ret);
+char* SSLSocket_get_version_string(int version);
+void SSL_CTX_msg_callback(
+ int write_p,
+ int version,
+ int content_type,
+ const void* buf, size_t len,
+ SSL* ssl, void* arg);
+int pem_passwd_cb(char* buf, int size, int rwflag, void* userdata);
+int SSL_create_mutex(ssl_mutex_type* mutex);
+int SSL_lock_mutex(ssl_mutex_type* mutex);
+int SSL_unlock_mutex(ssl_mutex_type* mutex);
+void SSL_destroy_mutex(ssl_mutex_type* mutex);
+#if (OPENSSL_VERSION_NUMBER >= 0x010000000)
+extern void SSLThread_id(CRYPTO_THREADID *id);
+#else
+extern unsigned long SSLThread_id(void);
+#endif
+extern void SSLLocks_callback(int mode, int n, const char *file, int line);
+int SSLSocket_createContext(networkHandles* net, MQTTClient_SSLOptions* opts);
+void SSLSocket_destroyContext(networkHandles* net);
+void SSLSocket_addPendingRead(int sock);
+
+/* 1 ~ we are responsible for initializing openssl; 0 ~ openssl init is done externally */
+static int handle_openssl_init = 1;
+static ssl_mutex_type* sslLocks = NULL;
+static ssl_mutex_type sslCoreMutex;
+
+#if defined(WIN32) || defined(WIN64)
+#define iov_len len
+#define iov_base buf
+#endif
+
+/**
+ * Gets the specific error corresponding to SOCKET_ERROR
+ * @param aString the function that was being used when the error occurred
+ * @param sock the socket on which the error occurred
+ * @return the specific TCP error code
+ */
+int SSLSocket_error(char* aString, SSL* ssl, int sock, int rc)
+{
+ int error;
+
+ FUNC_ENTRY;
+ if (ssl)
+ error = SSL_get_error(ssl, rc);
+ else
+ error = ERR_get_error();
+ if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
+ {
+ Log(TRACE_MIN, -1, "SSLSocket error WANT_READ/WANT_WRITE");
+ }
+ else
+ {
+ static char buf[120];
+
+ if (strcmp(aString, "shutdown") != 0)
+ Log(TRACE_MIN, -1, "SSLSocket error %s(%d) in %s for socket %d rc %d errno %d %s\n", buf, error, aString, sock, rc, errno, strerror(errno));
+ ERR_print_errors_fp(stderr);
+ if (error == SSL_ERROR_SSL || error == SSL_ERROR_SYSCALL)
+ error = SSL_FATAL;
+ }
+ FUNC_EXIT_RC(error);
+ return error;
+}
+
+static struct
+{
+ int code;
+ char* string;
+}
+X509_message_table[] =
+{
+ { X509_V_OK, "X509_V_OK" },
+ { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT" },
+ { X509_V_ERR_UNABLE_TO_GET_CRL, "X509_V_ERR_UNABLE_TO_GET_CRL" },
+ { X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE, "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE" },
+ { X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE, "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE" },
+ { X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY" },
+ { X509_V_ERR_CERT_SIGNATURE_FAILURE, "X509_V_ERR_CERT_SIGNATURE_FAILURE" },
+ { X509_V_ERR_CRL_SIGNATURE_FAILURE, "X509_V_ERR_CRL_SIGNATURE_FAILURE" },
+ { X509_V_ERR_CERT_NOT_YET_VALID, "X509_V_ERR_CERT_NOT_YET_VALID" },
+ { X509_V_ERR_CERT_HAS_EXPIRED, "X509_V_ERR_CERT_HAS_EXPIRED" },
+ { X509_V_ERR_CRL_NOT_YET_VALID, "X509_V_ERR_CRL_NOT_YET_VALID" },
+ { X509_V_ERR_CRL_HAS_EXPIRED, "X509_V_ERR_CRL_HAS_EXPIRED" },
+ { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD, "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD" },
+ { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD, "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD" },
+ { X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD, "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD" },
+ { X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD, "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD" },
+ { X509_V_ERR_OUT_OF_MEM, "X509_V_ERR_OUT_OF_MEM" },
+ { X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT" },
+ { X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN" },
+ { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY" },
+ { X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE" },
+ { X509_V_ERR_CERT_CHAIN_TOO_LONG, "X509_V_ERR_CERT_CHAIN_TOO_LONG" },
+ { X509_V_ERR_CERT_REVOKED, "X509_V_ERR_CERT_REVOKED" },
+ { X509_V_ERR_INVALID_CA, "X509_V_ERR_INVALID_CA" },
+ { X509_V_ERR_PATH_LENGTH_EXCEEDED, "X509_V_ERR_PATH_LENGTH_EXCEEDED" },
+ { X509_V_ERR_INVALID_PURPOSE, "X509_V_ERR_INVALID_PURPOSE" },
+ { X509_V_ERR_CERT_UNTRUSTED, "X509_V_ERR_CERT_UNTRUSTED" },
+ { X509_V_ERR_CERT_REJECTED, "X509_V_ERR_CERT_REJECTED" },
+ { X509_V_ERR_SUBJECT_ISSUER_MISMATCH, "X509_V_ERR_SUBJECT_ISSUER_MISMATCH" },
+ { X509_V_ERR_AKID_SKID_MISMATCH, "X509_V_ERR_AKID_SKID_MISMATCH" },
+ { X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH, "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH" },
+ { X509_V_ERR_KEYUSAGE_NO_CERTSIGN, "X509_V_ERR_KEYUSAGE_NO_CERTSIGN" },
+ { X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER" },
+ { X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION" },
+ { X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN" },
+ { X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION" },
+ { X509_V_ERR_INVALID_NON_CA, "X509_V_ERR_INVALID_NON_CA" },
+ { X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED" },
+ { X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE" },
+ { X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED" },
+ { X509_V_ERR_INVALID_EXTENSION, "X509_V_ERR_INVALID_EXTENSION" },
+ { X509_V_ERR_INVALID_POLICY_EXTENSION, "X509_V_ERR_INVALID_POLICY_EXTENSION" },
+ { X509_V_ERR_NO_EXPLICIT_POLICY, "X509_V_ERR_NO_EXPLICIT_POLICY" },
+ { X509_V_ERR_UNNESTED_RESOURCE, "X509_V_ERR_UNNESTED_RESOURCE" },
+#if defined(X509_V_ERR_DIFFERENT_CRL_SCOPE)
+ { X509_V_ERR_DIFFERENT_CRL_SCOPE, "X509_V_ERR_DIFFERENT_CRL_SCOPE" },
+ { X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE" },
+ { X509_V_ERR_PERMITTED_VIOLATION, "X509_V_ERR_PERMITTED_VIOLATION" },
+ { X509_V_ERR_EXCLUDED_VIOLATION, "X509_V_ERR_EXCLUDED_VIOLATION" },
+ { X509_V_ERR_SUBTREE_MINMAX, "X509_V_ERR_SUBTREE_MINMAX" },
+ { X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE" },
+ { X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX" },
+ { X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX" },
+#endif
+};
+
+#if !defined(ARRAY_SIZE)
+/**
+ * Macro to calculate the number of entries in an array
+ */
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+char* SSL_get_verify_result_string(int rc)
+{
+ int i;
+ char* retstring = "undef";
+
+ for (i = 0; i < ARRAY_SIZE(X509_message_table); ++i)
+ {
+ if (X509_message_table[i].code == rc)
+ {
+ retstring = X509_message_table[i].string;
+ break;
+ }
+ }
+ return retstring;
+}
+
+
+void SSL_CTX_info_callback(const SSL* ssl, int where, int ret)
+{
+ if (where & SSL_CB_LOOP)
+ {
+ Log(TRACE_PROTOCOL, 1, "SSL state %s:%s:%s",
+ (where & SSL_ST_CONNECT) ? "connect" : (where & SSL_ST_ACCEPT) ? "accept" : "undef",
+ SSL_state_string_long(ssl), SSL_get_cipher_name(ssl));
+ }
+ else if (where & SSL_CB_EXIT)
+ {
+ Log(TRACE_PROTOCOL, 1, "SSL %s:%s",
+ (where & SSL_ST_CONNECT) ? "connect" : (where & SSL_ST_ACCEPT) ? "accept" : "undef",
+ SSL_state_string_long(ssl));
+ }
+ else if (where & SSL_CB_ALERT)
+ {
+ Log(TRACE_PROTOCOL, 1, "SSL alert %s:%s:%s",
+ (where & SSL_CB_READ) ? "read" : "write",
+ SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+ }
+ else if (where & SSL_CB_HANDSHAKE_START)
+ {
+ Log(TRACE_PROTOCOL, 1, "SSL handshake started %s:%s:%s",
+ (where & SSL_CB_READ) ? "read" : "write",
+ SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+ }
+ else if (where & SSL_CB_HANDSHAKE_DONE)
+ {
+ Log(TRACE_PROTOCOL, 1, "SSL handshake done %s:%s:%s",
+ (where & SSL_CB_READ) ? "read" : "write",
+ SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+ Log(TRACE_PROTOCOL, 1, "SSL certificate verification: %s",
+ SSL_get_verify_result_string(SSL_get_verify_result(ssl)));
+ }
+ else
+ {
+ Log(TRACE_PROTOCOL, 1, "SSL state %s:%s:%s", SSL_state_string_long(ssl),
+ SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+ }
+}
+
+
+char* SSLSocket_get_version_string(int version)
+{
+ int i;
+ static char buf[20];
+ char* retstring = NULL;
+ static struct
+ {
+ int code;
+ char* string;
+ }
+ version_string_table[] =
+ {
+ { SSL2_VERSION, "SSL 2.0" },
+ { SSL3_VERSION, "SSL 3.0" },
+ { TLS1_VERSION, "TLS 1.0" },
+#if defined(TLS2_VERSION)
+ { TLS2_VERSION, "TLS 1.1" },
+#endif
+#if defined(TLS3_VERSION)
+ { TLS3_VERSION, "TLS 1.2" },
+#endif
+ };
+
+ for (i = 0; i < ARRAY_SIZE(version_string_table); ++i)
+ {
+ if (version_string_table[i].code == version)
+ {
+ retstring = version_string_table[i].string;
+ break;
+ }
+ }
+
+ if (retstring == NULL)
+ {
+ sprintf(buf, "%i", version);
+ retstring = buf;
+ }
+ return retstring;
+}
+
+
+void SSL_CTX_msg_callback(int write_p, int version, int content_type, const void* buf, size_t len,
+ SSL* ssl, void* arg)
+{
+
+/*
+called by the SSL/TLS library for a protocol message, the function arguments have the following meaning:
+
+write_p
+This flag is 0 when a protocol message has been received and 1 when a protocol message has been sent.
+
+version
+The protocol version according to which the protocol message is interpreted by the library. Currently, this is one of SSL2_VERSION, SSL3_VERSION and TLS1_VERSION (for SSL 2.0, SSL 3.0 and TLS 1.0, respectively).
+
+content_type
+In the case of SSL 2.0, this is always 0. In the case of SSL 3.0 or TLS 1.0, this is one of the ContentType values defined in the protocol specification (change_cipher_spec(20), alert(21), handshake(22); but never application_data(23) because the callback will only be called for protocol messages).
+
+buf, len
+buf points to a buffer containing the protocol message, which consists of len bytes. The buffer is no longer valid after the callback function has returned.
+
+ssl
+The SSL object that received or sent the message.
+
+arg
+The user-defined argument optionally defined by SSL_CTX_set_msg_callback_arg() or SSL_set_msg_callback_arg().
+
+*/
+
+ Log(TRACE_PROTOCOL, -1, "%s %s %d buflen %d", (write_p ? "sent" : "received"),
+ SSLSocket_get_version_string(version),
+ content_type, (int)len);
+}
+
+
+int pem_passwd_cb(char* buf, int size, int rwflag, void* userdata)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (!rwflag)
+ {
+ strncpy(buf, (char*)(userdata), size);
+ buf[size-1] = '\0';
+ rc = (int)strlen(buf);
+ }
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+int SSL_create_mutex(ssl_mutex_type* mutex)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ *mutex = CreateMutex(NULL, 0, NULL);
+#else
+ rc = pthread_mutex_init(mutex, NULL);
+#endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+int SSL_lock_mutex(ssl_mutex_type* mutex)
+{
+ int rc = -1;
+
+ /* don't add entry/exit trace points, as trace gets lock too, and it might happen quite frequently */
+#if defined(WIN32) || defined(WIN64)
+ if (WaitForSingleObject(*mutex, INFINITE) != WAIT_FAILED)
+#else
+ if ((rc = pthread_mutex_lock(mutex)) == 0)
+#endif
+ rc = 0;
+
+ return rc;
+}
+
+int SSL_unlock_mutex(ssl_mutex_type* mutex)
+{
+ int rc = -1;
+
+ /* don't add entry/exit trace points, as trace gets lock too, and it might happen quite frequently */
+#if defined(WIN32) || defined(WIN64)
+ if (ReleaseMutex(*mutex) != 0)
+#else
+ if ((rc = pthread_mutex_unlock(mutex)) == 0)
+#endif
+ rc = 0;
+
+ return rc;
+}
+
+void SSL_destroy_mutex(ssl_mutex_type* mutex)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ rc = CloseHandle(*mutex);
+#else
+ rc = pthread_mutex_destroy(mutex);
+#endif
+ FUNC_EXIT_RC(rc);
+}
+
+
+
+#if (OPENSSL_VERSION_NUMBER >= 0x010000000)
+extern void SSLThread_id(CRYPTO_THREADID *id)
+{
+#if defined(WIN32) || defined(WIN64)
+ CRYPTO_THREADID_set_numeric(id, (unsigned long)GetCurrentThreadId());
+#else
+ CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
+#endif
+}
+#else
+extern unsigned long SSLThread_id(void)
+{
+#if defined(WIN32) || defined(WIN64)
+ return (unsigned long)GetCurrentThreadId();
+#else
+ return (unsigned long)pthread_self();
+#endif
+}
+#endif
+
+extern void SSLLocks_callback(int mode, int n, const char *file, int line)
+{
+ if (sslLocks)
+ {
+ if (mode & CRYPTO_LOCK)
+ SSL_lock_mutex(&sslLocks[n]);
+ else
+ SSL_unlock_mutex(&sslLocks[n]);
+ }
+}
+
+
+void SSLSocket_handleOpensslInit(int bool_value)
+{
+ handle_openssl_init = bool_value;
+}
+
+
+int SSLSocket_initialize(void)
+{
+ int rc = 0;
+ /*int prc;*/
+ int i;
+ int lockMemSize;
+
+ FUNC_ENTRY;
+
+ if (handle_openssl_init)
+ {
+ if ((rc = SSL_library_init()) != 1)
+ rc = -1;
+
+ ERR_load_crypto_strings();
+ SSL_load_error_strings();
+
+ /* OpenSSL 0.9.8o and 1.0.0a and later added SHA2 algorithms to SSL_library_init().
+ Applications which need to use SHA2 in earlier versions of OpenSSL should call
+ OpenSSL_add_all_algorithms() as well. */
+
+ OpenSSL_add_all_algorithms();
+
+ lockMemSize = CRYPTO_num_locks() * sizeof(ssl_mutex_type);
+
+ sslLocks = malloc(lockMemSize);
+ if (!sslLocks)
+ {
+ rc = -1;
+ goto exit;
+ }
+ else
+ memset(sslLocks, 0, lockMemSize);
+
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ {
+ /* prc = */SSL_create_mutex(&sslLocks[i]);
+ }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x010000000)
+ CRYPTO_THREADID_set_callback(SSLThread_id);
+#else
+ CRYPTO_set_id_callback(SSLThread_id);
+#endif
+ CRYPTO_set_locking_callback(SSLLocks_callback);
+
+ }
+
+ SSL_create_mutex(&sslCoreMutex);
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+void SSLSocket_terminate(void)
+{
+ FUNC_ENTRY;
+
+ if (handle_openssl_init)
+ {
+ EVP_cleanup();
+ ERR_free_strings();
+ CRYPTO_set_locking_callback(NULL);
+ if (sslLocks)
+ {
+ int i = 0;
+
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ {
+ SSL_destroy_mutex(&sslLocks[i]);
+ }
+ free(sslLocks);
+ }
+ }
+
+ SSL_destroy_mutex(&sslCoreMutex);
+
+ FUNC_EXIT;
+}
+
+int SSLSocket_createContext(networkHandles* net, MQTTClient_SSLOptions* opts)
+{
+ int rc = 1;
+ const char* ciphers = NULL;
+
+ FUNC_ENTRY;
+ if (net->ctx == NULL)
+ {
+ int sslVersion = MQTT_SSL_VERSION_DEFAULT;
+ if (opts->struct_version >= 1) sslVersion = opts->sslVersion;
+/* SSL_OP_NO_TLSv1_1 is defined in ssl.h if the library version supports TLSv1.1.
+ * OPENSSL_NO_TLS1 is defined in opensslconf.h or on the compiler command line
+ * if TLS1.x was removed at OpenSSL library build time via Configure options.
+ */
+ switch (sslVersion)
+ {
+ case MQTT_SSL_VERSION_DEFAULT:
+ net->ctx = SSL_CTX_new(SSLv23_client_method()); /* SSLv23 for compatibility with SSLv2, SSLv3 and TLSv1 */
+ break;
+#if defined(SSL_OP_NO_TLSv1) && !defined(OPENSSL_NO_TLS1)
+ case MQTT_SSL_VERSION_TLS_1_0:
+ net->ctx = SSL_CTX_new(TLSv1_client_method());
+ break;
+#endif
+#if defined(SSL_OP_NO_TLSv1_1) && !defined(OPENSSL_NO_TLS1)
+ case MQTT_SSL_VERSION_TLS_1_1:
+ net->ctx = SSL_CTX_new(TLSv1_1_client_method());
+ break;
+#endif
+#if defined(SSL_OP_NO_TLSv1_2) && !defined(OPENSSL_NO_TLS1)
+ case MQTT_SSL_VERSION_TLS_1_2:
+ net->ctx = SSL_CTX_new(TLSv1_2_client_method());
+ break;
+#endif
+ default:
+ break;
+ }
+ if (net->ctx == NULL)
+ {
+ SSLSocket_error("SSL_CTX_new", NULL, net->socket, rc);
+ goto exit;
+ }
+ }
+
+ if (opts->keyStore)
+ {
+ if ((rc = SSL_CTX_use_certificate_chain_file(net->ctx, opts->keyStore)) != 1)
+ {
+ SSLSocket_error("SSL_CTX_use_certificate_chain_file", NULL, net->socket, rc);
+ goto free_ctx; /*If we can't load the certificate (chain) file then loading the privatekey won't work either as it needs a matching cert already loaded */
+ }
+
+ if (opts->privateKey == NULL)
+ opts->privateKey = opts->keyStore; /* the privateKey can be included in the keyStore */
+
+ if (opts->privateKeyPassword != NULL)
+ {
+ SSL_CTX_set_default_passwd_cb(net->ctx, pem_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(net->ctx, (void*)opts->privateKeyPassword);
+ }
+
+ /* support for ASN.1 == DER format? DER can contain only one certificate? */
+ rc = SSL_CTX_use_PrivateKey_file(net->ctx, opts->privateKey, SSL_FILETYPE_PEM);
+ if (opts->privateKey == opts->keyStore)
+ opts->privateKey = NULL;
+ if (rc != 1)
+ {
+ SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, net->socket, rc);
+ goto free_ctx;
+ }
+ }
+
+ if (opts->trustStore)
+ {
+ if ((rc = SSL_CTX_load_verify_locations(net->ctx, opts->trustStore, NULL)) != 1)
+ {
+ SSLSocket_error("SSL_CTX_load_verify_locations", NULL, net->socket, rc);
+ goto free_ctx;
+ }
+ }
+ else if ((rc = SSL_CTX_set_default_verify_paths(net->ctx)) != 1)
+ {
+ SSLSocket_error("SSL_CTX_set_default_verify_paths", NULL, net->socket, rc);
+ goto free_ctx;
+ }
+
+ if (opts->enabledCipherSuites == NULL)
+ ciphers = "DEFAULT";
+ else
+ ciphers = opts->enabledCipherSuites;
+
+ if ((rc = SSL_CTX_set_cipher_list(net->ctx, ciphers)) != 1)
+ {
+ SSLSocket_error("SSL_CTX_set_cipher_list", NULL, net->socket, rc);
+ goto free_ctx;
+ }
+
+ SSL_CTX_set_mode(net->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ goto exit;
+free_ctx:
+ SSL_CTX_free(net->ctx);
+ net->ctx = NULL;
+
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+int SSLSocket_setSocketForSSL(networkHandles* net, MQTTClient_SSLOptions* opts, char* hostname)
+{
+ int rc = 1;
+
+ FUNC_ENTRY;
+
+ if (net->ctx != NULL || (rc = SSLSocket_createContext(net, opts)) == 1)
+ {
+ int i;
+
+ SSL_CTX_set_info_callback(net->ctx, SSL_CTX_info_callback);
+ SSL_CTX_set_msg_callback(net->ctx, SSL_CTX_msg_callback);
+ if (opts->enableServerCertAuth)
+ SSL_CTX_set_verify(net->ctx, SSL_VERIFY_PEER, NULL);
+
+ net->ssl = SSL_new(net->ctx);
+
+ /* Log all ciphers available to the SSL sessions (loaded in ctx) */
+ for (i = 0; ;i++)
+ {
+ const char* cipher = SSL_get_cipher_list(net->ssl, i);
+ if (cipher == NULL)
+ break;
+ Log(TRACE_PROTOCOL, 1, "SSL cipher available: %d:%s", i, cipher);
+ }
+ if ((rc = SSL_set_fd(net->ssl, net->socket)) != 1)
+ SSLSocket_error("SSL_set_fd", net->ssl, net->socket, rc);
+
+ if ((rc = SSL_set_tlsext_host_name(net->ssl, hostname)) != 1)
+ SSLSocket_error("SSL_set_tlsext_host_name", NULL, net->socket, rc);
+ }
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+/*
+ * Return value: 1 - success, TCPSOCKET_INTERRUPTED - try again, anything else is failure
+ */
+int SSLSocket_connect(SSL* ssl, int sock, char* hostname, int verify)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+
+ rc = SSL_connect(ssl);
+ if (rc != 1)
+ {
+ int error;
+ error = SSLSocket_error("SSL_connect", ssl, sock, rc);
+ if (error == SSL_FATAL)
+ rc = error;
+ if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
+ rc = TCPSOCKET_INTERRUPTED;
+ }
+#if (OPENSSL_VERSION_NUMBER >= 0x010002000) /* 1.0.2 and later */
+ else if (verify == 1)
+ {
+ char* peername = NULL;
+ int port;
+ char* addr = NULL;
+
+ X509* cert = SSL_get_peer_certificate(ssl);
+ addr = MQTTProtocol_addressPort(hostname, &port);
+
+ rc = X509_check_host(cert, addr, strlen(addr), 0, &peername);
+ if (rc == 0)
+ rc = SOCKET_ERROR;
+ Log(TRACE_MIN, -1, "rc from X509_check_host is %d", rc);
+ Log(TRACE_MIN, -1, "peername from X509_check_host is %s", peername);
+
+ if (cert)
+ X509_free(cert);
+ if (addr != hostname)
+ free(addr);
+ }
+#endif
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+/**
+ * Reads one byte from a socket
+ * @param socket the socket to read from
+ * @param c the character read, returned
+ * @return completion code
+ */
+int SSLSocket_getch(SSL* ssl, int socket, char* c)
+{
+ int rc = SOCKET_ERROR;
+
+ FUNC_ENTRY;
+ if ((rc = SocketBuffer_getQueuedChar(socket, c)) != SOCKETBUFFER_INTERRUPTED)
+ goto exit;
+
+ if ((rc = SSL_read(ssl, c, (size_t)1)) < 0)
+ {
+ int err = SSLSocket_error("SSL_read - getch", ssl, socket, rc);
+ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
+ {
+ rc = TCPSOCKET_INTERRUPTED;
+ SocketBuffer_interrupted(socket, 0);
+ }
+ }
+ else if (rc == 0)
+ rc = SOCKET_ERROR; /* The return value from recv is 0 when the peer has performed an orderly shutdown. */
+ else if (rc == 1)
+ {
+ SocketBuffer_queueChar(socket, *c);
+ rc = TCPSOCKET_COMPLETE;
+ }
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+/**
+ * Attempts to read a number of bytes from a socket, non-blocking. If a previous read did not
+ * finish, then retrieve that data.
+ * @param socket the socket to read from
+ * @param bytes the number of bytes to read
+ * @param actual_len the actual number of bytes read
+ * @return completion code
+ */
+char *SSLSocket_getdata(SSL* ssl, int socket, size_t bytes, size_t* actual_len)
+{
+ int rc;
+ char* buf;
+
+ FUNC_ENTRY;
+ if (bytes == 0)
+ {
+ buf = SocketBuffer_complete(socket);
+ goto exit;
+ }
+
+ buf = SocketBuffer_getQueuedData(socket, bytes, actual_len);
+
+ if ((rc = SSL_read(ssl, buf + (*actual_len), (int)(bytes - (*actual_len)))) < 0)
+ {
+ rc = SSLSocket_error("SSL_read - getdata", ssl, socket, rc);
+ if (rc != SSL_ERROR_WANT_READ && rc != SSL_ERROR_WANT_WRITE)
+ {
+ buf = NULL;
+ goto exit;
+ }
+ }
+ else if (rc == 0) /* rc 0 means the other end closed the socket */
+ {
+ buf = NULL;
+ goto exit;
+ }
+ else
+ *actual_len += rc;
+
+ if (*actual_len == bytes)
+ {
+ SocketBuffer_complete(socket);
+ /* if we read the whole packet, there might still be data waiting in the SSL buffer, which
+ isn't picked up by select. So here we should check for any data remaining in the SSL buffer, and
+ if so, add this socket to a new "pending SSL reads" list.
+ */
+ if (SSL_pending(ssl) > 0) /* return no of bytes pending */
+ SSLSocket_addPendingRead(socket);
+ }
+ else /* we didn't read the whole packet */
+ {
+ SocketBuffer_interrupted(socket, *actual_len);
+ Log(TRACE_MAX, -1, "SSL_read: %d bytes expected but %d bytes now received", bytes, *actual_len);
+ }
+exit:
+ FUNC_EXIT;
+ return buf;
+}
+
+void SSLSocket_destroyContext(networkHandles* net)
+{
+ FUNC_ENTRY;
+ if (net->ctx)
+ SSL_CTX_free(net->ctx);
+ net->ctx = NULL;
+ FUNC_EXIT;
+}
+
+
+int SSLSocket_close(networkHandles* net)
+{
+ int rc = 1;
+ FUNC_ENTRY;
+ if (net->ssl) {
+ rc = SSL_shutdown(net->ssl);
+ SSL_free(net->ssl);
+ net->ssl = NULL;
+ }
+ SSLSocket_destroyContext(net);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/* No SSL_writev() provided by OpenSSL. Boo. */
+int SSLSocket_putdatas(SSL* ssl, int socket, char* buf0, size_t buf0len, int count, char** buffers, size_t* buflens, int* frees)
+{
+ int rc = 0;
+ int i;
+ char *ptr;
+ iobuf iovec;
+ int sslerror;
+
+ FUNC_ENTRY;
+ iovec.iov_len = (ULONG)buf0len;
+ for (i = 0; i < count; i++)
+ iovec.iov_len += (ULONG)buflens[i];
+
+ ptr = iovec.iov_base = (char *)malloc(iovec.iov_len);
+ memcpy(ptr, buf0, buf0len);
+ ptr += buf0len;
+ for (i = 0; i < count; i++)
+ {
+ memcpy(ptr, buffers[i], buflens[i]);
+ ptr += buflens[i];
+ }
+
+ SSL_lock_mutex(&sslCoreMutex);
+ if ((rc = SSL_write(ssl, iovec.iov_base, iovec.iov_len)) == iovec.iov_len)
+ rc = TCPSOCKET_COMPLETE;
+ else
+ {
+ sslerror = SSLSocket_error("SSL_write", ssl, socket, rc);
+
+ if (sslerror == SSL_ERROR_WANT_WRITE)
+ {
+ int* sockmem = (int*)malloc(sizeof(int));
+ int free = 1;
+
+ Log(TRACE_MIN, -1, "Partial write: incomplete write of %d bytes on SSL socket %d",
+ iovec.iov_len, socket);
+ SocketBuffer_pendingWrite(socket, ssl, 1, &iovec, &free, iovec.iov_len, 0);
+ *sockmem = socket;
+ ListAppend(s.write_pending, sockmem, sizeof(int));
+ FD_SET(socket, &(s.pending_wset));
+ rc = TCPSOCKET_INTERRUPTED;
+ }
+ else
+ rc = SOCKET_ERROR;
+ }
+ SSL_unlock_mutex(&sslCoreMutex);
+
+ if (rc != TCPSOCKET_INTERRUPTED)
+ free(iovec.iov_base);
+ else
+ {
+ int i;
+ free(buf0);
+ for (i = 0; i < count; ++i)
+ {
+ if (frees[i])
+ {
+ free(buffers[i]);
+ buffers[i] = NULL;
+ }
+ }
+ }
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+static List pending_reads = {NULL, NULL, NULL, 0, 0};
+
+void SSLSocket_addPendingRead(int sock)
+{
+ FUNC_ENTRY;
+ if (ListFindItem(&pending_reads, &sock, intcompare) == NULL) /* make sure we don't add the same socket twice */
+ {
+ int* psock = (int*)malloc(sizeof(sock));
+ *psock = sock;
+ ListAppend(&pending_reads, psock, sizeof(sock));
+ }
+ else
+ Log(TRACE_MIN, -1, "SSLSocket_addPendingRead: socket %d already in the list", sock);
+
+ FUNC_EXIT;
+}
+
+
+int SSLSocket_getPendingRead(void)
+{
+ int sock = -1;
+
+ if (pending_reads.count > 0)
+ {
+ sock = *(int*)(pending_reads.first->content);
+ ListRemoveHead(&pending_reads);
+ }
+ return sock;
+}
+
+
+int SSLSocket_continueWrite(pending_writes* pw)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if ((rc = SSL_write(pw->ssl, pw->iovecs[0].iov_base, pw->iovecs[0].iov_len)) == pw->iovecs[0].iov_len)
+ {
+ /* topic and payload buffers are freed elsewhere, when all references to them have been removed */
+ free(pw->iovecs[0].iov_base);
+ Log(TRACE_MIN, -1, "SSL continueWrite: partial write now complete for socket %d", pw->socket);
+ rc = 1;
+ }
+ else
+ {
+ int sslerror = SSLSocket_error("SSL_write", pw->ssl, pw->socket, rc);
+ if (sslerror == SSL_ERROR_WANT_WRITE)
+ rc = 0; /* indicate we haven't finished writing the payload yet */
+ }
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/SSLSocket.h b/tts-mqtt-test/src/paho-mqtt/SSLSocket.h
new file mode 100644
index 0000000..51ef9c4
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/SSLSocket.h
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs, Allan Stockdill-Mander - initial implementation
+ * Ian Craggs - SNI support
+ * Ian Craggs - post connect checks and CApath
+ *******************************************************************************/
+#if !defined(SSLSOCKET_H)
+#define SSLSOCKET_H
+
+#if defined(WIN32) || defined(WIN64)
+ #define ssl_mutex_type HANDLE
+#else
+ #include <pthread.h>
+ #include <semaphore.h>
+ #define ssl_mutex_type pthread_mutex_t
+#endif
+
+#include <openssl/ssl.h>
+#include "../paho-mqtt/SocketBuffer.h"
+#include "../paho-mqtt/Clients.h"
+
+#define URI_SSL "ssl://"
+
+/** if we should handle openssl initialization (bool_value == 1) or depend on it to be initalized externally (bool_value == 0) */
+void SSLSocket_handleOpensslInit(int bool_value);
+
+int SSLSocket_initialize(void);
+void SSLSocket_terminate(void);
+int SSLSocket_setSocketForSSL(networkHandles* net, MQTTClient_SSLOptions* opts, char* hostname);
+
+int SSLSocket_getch(SSL* ssl, int socket, char* c);
+char *SSLSocket_getdata(SSL* ssl, int socket, size_t bytes, size_t* actual_len);
+
+int SSLSocket_close(networkHandles* net);
+int SSLSocket_putdatas(SSL* ssl, int socket, char* buf0, size_t buf0len, int count, char** buffers, size_t* buflens, int* frees);
+int SSLSocket_connect(SSL* ssl, int sock, char* hostname, int verify);
+
+int SSLSocket_getPendingRead(void);
+int SSLSocket_continueWrite(pending_writes* pw);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/Socket.c b/tts-mqtt-test/src/paho-mqtt/Socket.c
new file mode 100644
index 0000000..1fdf4da
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Socket.c
@@ -0,0 +1,1013 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial implementation and documentation
+ * Ian Craggs - async client updates
+ * Ian Craggs - fix for bug 484496
+ * Juergen Kosel, Ian Craggs - fix for issue #135
+ * Ian Craggs - issue #217
+ * Ian Craggs - fix for issue #186
+ * Ian Craggs - remove StackTrace print debugging calls
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Socket related functions
+ *
+ * Some other related functions are in the SocketBuffer module
+ */
+
+
+#include "../paho-mqtt/Socket.h"
+
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/Messages.h"
+#include "../paho-mqtt/SocketBuffer.h"
+#include "../paho-mqtt/StackTrace.h"
+#if defined(OPENSSL)
+#include "../paho-mqtt/SSLSocket.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "../paho-mqtt/Heap.h"
+
+int Socket_setnonblocking(int sock);
+int Socket_error(char* aString, int sock);
+int Socket_addSocket(int newSd);
+int isReady(int socket, fd_set* read_set, fd_set* write_set);
+int Socket_writev(int socket, iobuf* iovecs, int count, unsigned long* bytes);
+int Socket_close_only(int socket);
+int Socket_continueWrite(int socket);
+int Socket_continueWrites(fd_set* pwset);
+char* Socket_getaddrname(struct sockaddr* sa, int sock);
+int Socket_abortWrite(int socket);
+
+#if defined(WIN32) || defined(WIN64)
+#define iov_len len
+#define iov_base buf
+#endif
+
+/**
+ * Structure to hold all socket data for the module
+ */
+Sockets s;
+static fd_set wset;
+
+/**
+ * Set a socket non-blocking, OS independently
+ * @param sock the socket to set non-blocking
+ * @return TCP call error code
+ */
+int Socket_setnonblocking(int sock)
+{
+ int rc;
+#if defined(WIN32) || defined(WIN64)
+ u_long flag = 1L;
+
+ FUNC_ENTRY;
+ rc = ioctl(sock, FIONBIO, &flag);
+#else
+ int flags;
+
+ FUNC_ENTRY;
+ if ((flags = fcntl(sock, F_GETFL, 0)))
+ flags = 0;
+ rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+#endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Gets the specific error corresponding to SOCKET_ERROR
+ * @param aString the function that was being used when the error occurred
+ * @param sock the socket on which the error occurred
+ * @return the specific TCP error code
+ */
+int Socket_error(char* aString, int sock)
+{
+#if defined(WIN32) || defined(WIN64)
+ int errno;
+#endif
+
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ errno = WSAGetLastError();
+#endif
+ if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != EWOULDBLOCK)
+ {
+ if (strcmp(aString, "shutdown") != 0 || (errno != ENOTCONN && errno != ECONNRESET))
+ Log(TRACE_MINIMUM, -1, "Socket error %s(%d) in %s for socket %d", strerror(errno), errno, aString, sock);
+ }
+ FUNC_EXIT_RC(errno);
+ return errno;
+}
+
+
+/**
+ * Initialize the socket module
+ */
+void Socket_outInitialize(void)
+{
+#if defined(WIN32) || defined(WIN64)
+ WORD winsockVer = 0x0202;
+ WSADATA wsd;
+
+ FUNC_ENTRY;
+ WSAStartup(winsockVer, &wsd);
+#else
+ FUNC_ENTRY;
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ SocketBuffer_initialize();
+ s.clientsds = ListInitialize();
+ s.connect_pending = ListInitialize();
+ s.write_pending = ListInitialize();
+ s.cur_clientsds = NULL;
+ FD_ZERO(&(s.rset)); /* Initialize the descriptor set */
+ FD_ZERO(&(s.pending_wset));
+ s.maxfdp1 = 0;
+ memcpy((void*)&(s.rset_saved), (void*)&(s.rset), sizeof(s.rset_saved));
+ FUNC_EXIT;
+}
+
+
+/**
+ * Terminate the socket module
+ */
+void Socket_outTerminate(void)
+{
+ FUNC_ENTRY;
+ ListFree(s.connect_pending);
+ ListFree(s.write_pending);
+ ListFree(s.clientsds);
+ SocketBuffer_terminate();
+#if defined(WIN32) || defined(WIN64)
+ WSACleanup();
+#endif
+ FUNC_EXIT;
+}
+
+
+/**
+ * Add a socket to the list of socket to check with select
+ * @param newSd the new socket to add
+ */
+int Socket_addSocket(int newSd)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (ListFindItem(s.clientsds, &newSd, intcompare) == NULL) /* make sure we don't add the same socket twice */
+ {
+ if (s.clientsds->count >= FD_SETSIZE)
+ {
+ Log(LOG_ERROR, -1, "addSocket: exceeded FD_SETSIZE %d", FD_SETSIZE);
+ rc = SOCKET_ERROR;
+ }
+ else
+ {
+ int* pnewSd = (int*)malloc(sizeof(newSd));
+ *pnewSd = newSd;
+ ListAppend(s.clientsds, pnewSd, sizeof(newSd));
+ FD_SET(newSd, &(s.rset_saved));
+ s.maxfdp1 = max(s.maxfdp1, newSd + 1);
+ rc = Socket_setnonblocking(newSd);
+ if (rc == SOCKET_ERROR)
+ Log(LOG_ERROR, -1, "addSocket: setnonblocking");
+ }
+ }
+ else
+ Log(LOG_ERROR, -1, "addSocket: socket %d already in the list", newSd);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Don't accept work from a client unless it is accepting work back, i.e. its socket is writeable
+ * this seems like a reasonable form of flow control, and practically, seems to work.
+ * @param socket the socket to check
+ * @param read_set the socket read set (see select doc)
+ * @param write_set the socket write set (see select doc)
+ * @return boolean - is the socket ready to go?
+ */
+int isReady(int socket, fd_set* read_set, fd_set* write_set)
+{
+ int rc = 1;
+
+ FUNC_ENTRY;
+ if (ListFindItem(s.connect_pending, &socket, intcompare) && FD_ISSET(socket, write_set))
+ ListRemoveItem(s.connect_pending, &socket, intcompare);
+ else
+ rc = FD_ISSET(socket, read_set) && FD_ISSET(socket, write_set) && Socket_noPendingWrites(socket);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Returns the next socket ready for communications as indicated by select
+ * @param more_work flag to indicate more work is waiting, and thus a timeout value of 0 should
+ * be used for the select
+ * @param tp the timeout to be used for the select, unless overridden
+ * @return the socket next ready, or 0 if none is ready
+ */
+int Socket_getReadySocket(int more_work, struct timeval *tp, mutex_type mutex)
+{
+ int rc = 0;
+ static struct timeval zero = {0L, 0L}; /* 0 seconds */
+ static struct timeval one = {1L, 0L}; /* 1 second */
+ struct timeval timeout = one;
+
+ FUNC_ENTRY;
+ Thread_lock_mutex(mutex);
+ if (s.clientsds->count == 0)
+ goto exit;
+
+ if (more_work)
+ timeout = zero;
+ else if (tp)
+ timeout = *tp;
+
+ while (s.cur_clientsds != NULL)
+ {
+ if (isReady(*((int*)(s.cur_clientsds->content)), &(s.rset), &wset))
+ break;
+ ListNextElement(s.clientsds, &s.cur_clientsds);
+ }
+
+ if (s.cur_clientsds == NULL)
+ {
+ int rc1;
+ fd_set pwset;
+
+ memcpy((void*)&(s.rset), (void*)&(s.rset_saved), sizeof(s.rset));
+ memcpy((void*)&(pwset), (void*)&(s.pending_wset), sizeof(pwset));
+ /* Prevent performance issue by unlocking the socket_mutex while waiting for a ready socket. */
+ Thread_unlock_mutex(mutex);
+ rc = select(s.maxfdp1, &(s.rset), &pwset, NULL, &timeout);
+ Thread_lock_mutex(mutex);
+ if (rc == SOCKET_ERROR)
+ {
+ Socket_error("read select", 0);
+ goto exit;
+ }
+ Log(TRACE_MAX, -1, "Return code %d from read select", rc);
+
+ if (Socket_continueWrites(&pwset) == SOCKET_ERROR)
+ {
+ rc = 0;
+ goto exit;
+ }
+
+ memcpy((void*)&wset, (void*)&(s.rset_saved), sizeof(wset));
+ if ((rc1 = select(s.maxfdp1, NULL, &(wset), NULL, &zero)) == SOCKET_ERROR)
+ {
+ Socket_error("write select", 0);
+ rc = rc1;
+ goto exit;
+ }
+ Log(TRACE_MAX, -1, "Return code %d from write select", rc1);
+
+ if (rc == 0 && rc1 == 0)
+ goto exit; /* no work to do */
+
+ s.cur_clientsds = s.clientsds->first;
+ while (s.cur_clientsds != NULL)
+ {
+ int cursock = *((int*)(s.cur_clientsds->content));
+ if (isReady(cursock, &(s.rset), &wset))
+ break;
+ ListNextElement(s.clientsds, &s.cur_clientsds);
+ }
+ }
+
+ if (s.cur_clientsds == NULL)
+ rc = 0;
+ else
+ {
+ rc = *((int*)(s.cur_clientsds->content));
+ ListNextElement(s.clientsds, &s.cur_clientsds);
+ }
+exit:
+ Thread_unlock_mutex(mutex);
+ FUNC_EXIT_RC(rc);
+ return rc;
+} /* end getReadySocket */
+
+
+/**
+ * Reads one byte from a socket
+ * @param socket the socket to read from
+ * @param c the character read, returned
+ * @return completion code
+ */
+int Socket_getch(int socket, char* c)
+{
+ int rc = SOCKET_ERROR;
+
+ FUNC_ENTRY;
+ if ((rc = SocketBuffer_getQueuedChar(socket, c)) != SOCKETBUFFER_INTERRUPTED)
+ goto exit;
+
+ if ((rc = recv(socket, c, (size_t)1, 0)) == SOCKET_ERROR)
+ {
+ int err = Socket_error("recv - getch", socket);
+ if (err == EWOULDBLOCK || err == EAGAIN)
+ {
+ rc = TCPSOCKET_INTERRUPTED;
+ SocketBuffer_interrupted(socket, 0);
+ }
+ }
+ else if (rc == 0)
+ rc = SOCKET_ERROR; /* The return value from recv is 0 when the peer has performed an orderly shutdown. */
+ else if (rc == 1)
+ {
+ SocketBuffer_queueChar(socket, *c);
+ rc = TCPSOCKET_COMPLETE;
+ }
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Attempts to read a number of bytes from a socket, non-blocking. If a previous read did not
+ * finish, then retrieve that data.
+ * @param socket the socket to read from
+ * @param bytes the number of bytes to read
+ * @param actual_len the actual number of bytes read
+ * @return completion code
+ */
+char *Socket_getdata(int socket, size_t bytes, size_t* actual_len)
+{
+ int rc;
+ char* buf;
+
+ FUNC_ENTRY;
+ if (bytes == 0)
+ {
+ buf = SocketBuffer_complete(socket);
+ goto exit;
+ }
+
+ buf = SocketBuffer_getQueuedData(socket, bytes, actual_len);
+
+ if ((rc = recv(socket, buf + (*actual_len), (int)(bytes - (*actual_len)), 0)) == SOCKET_ERROR)
+ {
+ rc = Socket_error("recv - getdata", socket);
+ if (rc != EAGAIN && rc != EWOULDBLOCK)
+ {
+ buf = NULL;
+ goto exit;
+ }
+ }
+ else if (rc == 0) /* rc 0 means the other end closed the socket, albeit "gracefully" */
+ {
+ buf = NULL;
+ goto exit;
+ }
+ else
+ *actual_len += rc;
+
+ if (*actual_len == bytes)
+ SocketBuffer_complete(socket);
+ else /* we didn't read the whole packet */
+ {
+ SocketBuffer_interrupted(socket, *actual_len);
+ Log(TRACE_MAX, -1, "%d bytes expected but %d bytes now received", bytes, *actual_len);
+ }
+exit:
+ FUNC_EXIT;
+ return buf;
+}
+
+
+/**
+ * Indicate whether any data is pending outbound for a socket.
+ * @return boolean - true == data pending.
+ */
+int Socket_noPendingWrites(int socket)
+{
+ int cursock = socket;
+ return ListFindItem(s.write_pending, &cursock, intcompare) == NULL;
+}
+
+
+/**
+ * Attempts to write a series of iovec buffers to a socket in *one* system call so that
+ * they are sent as one packet.
+ * @param socket the socket to write to
+ * @param iovecs an array of buffers to write
+ * @param count number of buffers in iovecs
+ * @param bytes number of bytes actually written returned
+ * @return completion code, especially TCPSOCKET_INTERRUPTED
+ */
+int Socket_writev(int socket, iobuf* iovecs, int count, unsigned long* bytes)
+{
+ int rc;
+
+ FUNC_ENTRY;
+ *bytes = 0L;
+#if defined(WIN32) || defined(WIN64)
+ rc = WSASend(socket, iovecs, count, (LPDWORD)bytes, 0, NULL, NULL);
+ if (rc == SOCKET_ERROR)
+ {
+ int err = Socket_error("WSASend - putdatas", socket);
+ if (err == EWOULDBLOCK || err == EAGAIN)
+ rc = TCPSOCKET_INTERRUPTED;
+ }
+#else
+/*#define TCPSOCKET_INTERRUPTED_TESTING
+This section forces the occasional return of TCPSOCKET_INTERRUPTED,
+for testing purposes only!
+*/
+#if defined(TCPSOCKET_INTERRUPTED_TESTING)
+ static int i = 0;
+ if (++i >= 10 && i < 21)
+ {
+ if (1)
+ {
+ printf("Deliberately simulating TCPSOCKET_INTERRUPTED\n");
+ rc = TCPSOCKET_INTERRUPTED; /* simulate a network wait */
+ }
+ else
+ {
+ printf("Deliberately simulating SOCKET_ERROR\n");
+ rc = SOCKET_ERROR;
+ }
+ /* should *bytes always be 0? */
+ if (i == 20)
+ {
+ printf("Shutdown socket\n");
+ shutdown(socket, SHUT_WR);
+ }
+ }
+ else
+ {
+#endif
+ rc = writev(socket, iovecs, count);
+ if (rc == SOCKET_ERROR)
+ {
+ int err = Socket_error("writev - putdatas", socket);
+ if (err == EWOULDBLOCK || err == EAGAIN)
+ rc = TCPSOCKET_INTERRUPTED;
+ }
+ else
+ *bytes = rc;
+#if defined(TCPSOCKET_INTERRUPTED_TESTING)
+ }
+#endif
+#endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Attempts to write a series of buffers to a socket in *one* system call so that they are
+ * sent as one packet.
+ * @param socket the socket to write to
+ * @param buf0 the first buffer
+ * @param buf0len the length of data in the first buffer
+ * @param count number of buffers
+ * @param buffers an array of buffers to write
+ * @param buflens an array of corresponding buffer lengths
+ * @return completion code, especially TCPSOCKET_INTERRUPTED
+ */
+int Socket_putdatas(int socket, char* buf0, size_t buf0len, int count, char** buffers, size_t* buflens, int* frees)
+{
+ unsigned long bytes = 0L;
+ iobuf iovecs[5];
+ int frees1[5];
+ int rc = TCPSOCKET_INTERRUPTED, i;
+ size_t total = buf0len;
+
+ FUNC_ENTRY;
+ if (!Socket_noPendingWrites(socket))
+ {
+ Log(LOG_SEVERE, -1, "Trying to write to socket %d for which there is already pending output", socket);
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+
+ for (i = 0; i < count; i++)
+ total += buflens[i];
+
+ iovecs[0].iov_base = buf0;
+ iovecs[0].iov_len = (ULONG)buf0len;
+ frees1[0] = 1; /* this buffer should be freed by SocketBuffer if the write is interrupted */
+ for (i = 0; i < count; i++)
+ {
+ iovecs[i+1].iov_base = buffers[i];
+ iovecs[i+1].iov_len = (ULONG)buflens[i];
+ frees1[i+1] = frees[i];
+ }
+
+ if ((rc = Socket_writev(socket, iovecs, count+1, &bytes)) != SOCKET_ERROR)
+ {
+ if (bytes == total)
+ rc = TCPSOCKET_COMPLETE;
+ else
+ {
+ int* sockmem = (int*)malloc(sizeof(int));
+ Log(TRACE_MIN, -1, "Partial write: %ld bytes of %d actually written on socket %d",
+ bytes, total, socket);
+#if defined(OPENSSL)
+ SocketBuffer_pendingWrite(socket, NULL, count+1, iovecs, frees1, total, bytes);
+#else
+ SocketBuffer_pendingWrite(socket, count+1, iovecs, frees1, total, bytes);
+#endif
+ *sockmem = socket;
+ ListAppend(s.write_pending, sockmem, sizeof(int));
+ FD_SET(socket, &(s.pending_wset));
+ rc = TCPSOCKET_INTERRUPTED;
+ }
+ }
+exit:
+#if 0
+ if (rc == TCPSOCKET_INTERRUPTED)
+ {
+ Log(LOG_ERROR, -1, "Socket_putdatas: TCPSOCKET_INTERRUPTED");
+ }
+#endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Add a socket to the pending write list, so that it is checked for writing in select. This is used
+ * in connect processing when the TCP connect is incomplete, as we need to check the socket for both
+ * ready to read and write states.
+ * @param socket the socket to add
+ */
+void Socket_addPendingWrite(int socket)
+{
+ FD_SET(socket, &(s.pending_wset));
+}
+
+
+/**
+ * Clear a socket from the pending write list - if one was added with Socket_addPendingWrite
+ * @param socket the socket to remove
+ */
+void Socket_clearPendingWrite(int socket)
+{
+ if (FD_ISSET(socket, &(s.pending_wset)))
+ FD_CLR(socket, &(s.pending_wset));
+}
+
+
+/**
+ * Close a socket without removing it from the select list.
+ * @param socket the socket to close
+ * @return completion code
+ */
+int Socket_close_only(int socket)
+{
+ int rc;
+
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ if (shutdown(socket, SD_BOTH) == SOCKET_ERROR)
+ Socket_error("shutdown", socket);
+ if ((rc = closesocket(socket)) == SOCKET_ERROR)
+ Socket_error("close", socket);
+#else
+ if (shutdown(socket, SHUT_WR) == SOCKET_ERROR)
+ Socket_error("shutdown", socket);
+ if ((rc = recv(socket, NULL, (size_t)0, 0)) == SOCKET_ERROR)
+ Socket_error("shutdown", socket);
+ if ((rc = close(socket)) == SOCKET_ERROR)
+ Socket_error("close", socket);
+#endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Close a socket and remove it from the select list.
+ * @param socket the socket to close
+ * @return completion code
+ */
+void Socket_close(int socket)
+{
+ FUNC_ENTRY;
+ Socket_close_only(socket);
+ FD_CLR(socket, &(s.rset_saved));
+ if (FD_ISSET(socket, &(s.pending_wset)))
+ FD_CLR(socket, &(s.pending_wset));
+ if (s.cur_clientsds != NULL && *(int*)(s.cur_clientsds->content) == socket)
+ s.cur_clientsds = s.cur_clientsds->next;
+ Socket_abortWrite(socket);
+ SocketBuffer_cleanup(socket);
+ ListRemoveItem(s.connect_pending, &socket, intcompare);
+ ListRemoveItem(s.write_pending, &socket, intcompare);
+
+ if (ListRemoveItem(s.clientsds, &socket, intcompare))
+ Log(TRACE_MIN, -1, "Removed socket %d", socket);
+ else
+ Log(LOG_ERROR, -1, "Failed to remove socket %d", socket);
+ if (socket + 1 >= s.maxfdp1)
+ {
+ /* now we have to reset s.maxfdp1 */
+ ListElement* cur_clientsds = NULL;
+
+ s.maxfdp1 = 0;
+ while (ListNextElement(s.clientsds, &cur_clientsds))
+ s.maxfdp1 = max(*((int*)(cur_clientsds->content)), s.maxfdp1);
+ ++(s.maxfdp1);
+ Log(TRACE_MAX, -1, "Reset max fdp1 to %d", s.maxfdp1);
+ }
+ FUNC_EXIT;
+}
+
+
+/**
+ * Create a new socket and TCP connect to an address/port
+ * @param addr the address string
+ * @param port the TCP port
+ * @param sock returns the new socket
+ * @return completion code
+ */
+int Socket_new(char* addr, int port, int* sock)
+{
+ int type = SOCK_STREAM;
+ struct sockaddr_in address;
+#if defined(AF_INET6)
+ struct sockaddr_in6 address6;
+#endif
+ int rc = SOCKET_ERROR;
+#if defined(WIN32) || defined(WIN64)
+ short family;
+#else
+ sa_family_t family = AF_INET;
+#endif
+ struct addrinfo *result = NULL;
+ struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
+
+ FUNC_ENTRY;
+ *sock = -1;
+ memset(&address6, '\0', sizeof(address6));
+
+ if (addr[0] == '[')
+ ++addr;
+
+ if ((rc = getaddrinfo(addr, NULL, &hints, &result)) == 0)
+ {
+ struct addrinfo* res = result;
+
+ while (res)
+ { /* prefer ip4 addresses */
+ if (res->ai_family == AF_INET || res->ai_next == NULL)
+ break;
+ res = res->ai_next;
+ }
+
+ if (res == NULL)
+ rc = -1;
+ else
+#if defined(AF_INET6)
+ if (res->ai_family == AF_INET6)
+ {
+ address6.sin6_port = htons(port);
+ address6.sin6_family = family = AF_INET6;
+ memcpy(&address6.sin6_addr, &((struct sockaddr_in6*)(res->ai_addr))->sin6_addr, sizeof(address6.sin6_addr));
+ }
+ else
+#endif
+ if (res->ai_family == AF_INET)
+ {
+ address.sin_port = htons(port);
+ address.sin_family = family = AF_INET;
+ address.sin_addr = ((struct sockaddr_in*)(res->ai_addr))->sin_addr;
+ }
+ else
+ rc = -1;
+
+ freeaddrinfo(result);
+ }
+ else
+ Log(LOG_ERROR, -1, "getaddrinfo failed for addr %s with rc %d", addr, rc);
+
+ if (rc != 0)
+ Log(LOG_ERROR, -1, "%s is not a valid IP address", addr);
+ else
+ {
+ *sock = (int)socket(family, type, 0);
+ if (*sock == INVALID_SOCKET)
+ rc = Socket_error("socket", *sock);
+ else
+ {
+#if defined(NOSIGPIPE)
+ int opt = 1;
+
+ if (setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0)
+ Log(LOG_ERROR, -1, "Could not set SO_NOSIGPIPE for socket %d", *sock);
+#endif
+/*#define SMALL_TCP_BUFFER_TESTING
+ This section sets the TCP send buffer to a small amount to provoke TCPSOCKET_INTERRUPTED
+ return codes from send, for testing only!
+*/
+#if defined(SMALL_TCP_BUFFER_TESTING)
+ if (1)
+ {
+ int optsend = 100; //2 * 1440;
+ printf("Setting optsend to %d\n", optsend);
+ if (setsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (void*)&optsend, sizeof(optsend)) != 0)
+ Log(LOG_ERROR, -1, "Could not set SO_SNDBUF for socket %d", *sock);
+ }
+#endif
+ Log(TRACE_MIN, -1, "New socket %d for %s, port %d", *sock, addr, port);
+ if (Socket_addSocket(*sock) == SOCKET_ERROR)
+ rc = Socket_error("addSocket", *sock);
+ else
+ {
+ /* this could complete immmediately, even though we are non-blocking */
+ if (family == AF_INET)
+ rc = connect(*sock, (struct sockaddr*)&address, sizeof(address));
+ #if defined(AF_INET6)
+ else
+ rc = connect(*sock, (struct sockaddr*)&address6, sizeof(address6));
+ #endif
+ if (rc == SOCKET_ERROR)
+ rc = Socket_error("connect", *sock);
+ if (rc == EINPROGRESS || rc == EWOULDBLOCK)
+ {
+ int* pnewSd = (int*)malloc(sizeof(int));
+ *pnewSd = *sock;
+ ListAppend(s.connect_pending, pnewSd, sizeof(int));
+ Log(TRACE_MIN, 15, "Connect pending");
+ }
+ }
+ /* Prevent socket leak by closing unusable sockets,
+ * as reported in
+ * https://github.com/eclipse/paho.mqtt.c/issues/135
+ */
+ if (rc != 0 && (rc != EINPROGRESS) && (rc != EWOULDBLOCK))
+ {
+ Socket_close(*sock); /* close socket and remove from our list of sockets */
+ *sock = -1; /* as initialized before */
+ }
+ }
+ }
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+static Socket_writeComplete* writecomplete = NULL;
+
+void Socket_setWriteCompleteCallback(Socket_writeComplete* mywritecomplete)
+{
+ writecomplete = mywritecomplete;
+}
+
+
+
+/**
+ * Continue an outstanding write for a particular socket
+ * @param socket that socket
+ * @return completion code: 0=incomplete, 1=complete, -1=socket error
+ */
+int Socket_continueWrite(int socket)
+{
+ int rc = 0;
+ pending_writes* pw;
+ unsigned long curbuflen = 0L, /* cumulative total of buffer lengths */
+ bytes = 0L;
+ int curbuf = -1, i;
+ iobuf iovecs1[5];
+
+ FUNC_ENTRY;
+ pw = SocketBuffer_getWrite(socket);
+
+#if defined(OPENSSL)
+ if (pw->ssl)
+ {
+ rc = SSLSocket_continueWrite(pw);
+ goto exit;
+ }
+#endif
+
+ for (i = 0; i < pw->count; ++i)
+ {
+ if (pw->bytes <= curbuflen)
+ { /* if previously written length is less than the buffer we are currently looking at,
+ add the whole buffer */
+ iovecs1[++curbuf].iov_len = pw->iovecs[i].iov_len;
+ iovecs1[curbuf].iov_base = pw->iovecs[i].iov_base;
+ }
+ else if (pw->bytes < curbuflen + pw->iovecs[i].iov_len)
+ { /* if previously written length is in the middle of the buffer we are currently looking at,
+ add some of the buffer */
+ size_t offset = pw->bytes - curbuflen;
+ iovecs1[++curbuf].iov_len = pw->iovecs[i].iov_len - (ULONG)offset;
+ iovecs1[curbuf].iov_base = (char*)pw->iovecs[i].iov_base + offset;
+ break;
+ }
+ curbuflen += pw->iovecs[i].iov_len;
+ }
+
+ if ((rc = Socket_writev(socket, iovecs1, curbuf+1, &bytes)) != SOCKET_ERROR)
+ {
+ pw->bytes += bytes;
+ if ((rc = (pw->bytes == pw->total)))
+ { /* topic and payload buffers are freed elsewhere, when all references to them have been removed */
+ for (i = 0; i < pw->count; i++)
+ {
+ if (pw->frees[i])
+ {
+ free(pw->iovecs[i].iov_base);
+ pw->iovecs[i].iov_base = NULL;
+ }
+ }
+ rc = 1; /* signal complete */
+ Log(TRACE_MIN, -1, "ContinueWrite: partial write now complete for socket %d", socket);
+ }
+ else
+ {
+ rc = 0; /* signal not complete */
+ Log(TRACE_MIN, -1, "ContinueWrite wrote +%lu bytes on socket %d", bytes, socket);
+ }
+ }
+ else /* if we got SOCKET_ERROR we need to clean up anyway - a partial write is no good anymore */
+ {
+ for (i = 0; i < pw->count; i++)
+ {
+ if (pw->frees[i])
+ {
+ free(pw->iovecs[i].iov_base);
+ pw->iovecs[i].iov_base = NULL;
+ }
+ }
+ }
+#if defined(OPENSSL)
+exit:
+#endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+/**
+ * Continue an outstanding write for a particular socket
+ * @param socket that socket
+ * @return completion code: 0=incomplete, 1=complete, -1=socket error
+ */
+int Socket_abortWrite(int socket)
+{
+ int i = -1, rc = 0;
+ pending_writes* pw;
+
+ FUNC_ENTRY;
+ if ((pw = SocketBuffer_getWrite(socket)) == NULL)
+ goto exit;
+
+#if defined(OPENSSL)
+ if (pw->ssl)
+ goto exit;
+#endif
+
+ for (i = 0; i < pw->count; i++)
+ {
+ if (pw->frees[i])
+ {
+ printf("cleaning in abortwrite for socket %d\n", socket);
+ free(pw->iovecs[i].iov_base);
+ }
+ }
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Continue any outstanding writes for a socket set
+ * @param pwset the set of sockets
+ * @return completion code
+ */
+int Socket_continueWrites(fd_set* pwset)
+{
+ int rc1 = 0;
+ ListElement* curpending = s.write_pending->first;
+
+ FUNC_ENTRY;
+ while (curpending)
+ {
+ int socket = *(int*)(curpending->content);
+ int rc = 0;
+
+ if (FD_ISSET(socket, pwset) && ((rc = Socket_continueWrite(socket)) != 0))
+ {
+ if (!SocketBuffer_writeComplete(socket))
+ Log(LOG_SEVERE, -1, "Failed to remove pending write from socket buffer list");
+ FD_CLR(socket, &(s.pending_wset));
+ if (!ListRemove(s.write_pending, curpending->content))
+ {
+ Log(LOG_SEVERE, -1, "Failed to remove pending write from list");
+ ListNextElement(s.write_pending, &curpending);
+ }
+ curpending = s.write_pending->current;
+
+ if (writecomplete)
+ (*writecomplete)(socket, rc);
+ }
+ else
+ ListNextElement(s.write_pending, &curpending);
+ }
+ FUNC_EXIT_RC(rc1);
+ return rc1;
+}
+
+
+/**
+ * Convert a numeric address to character string
+ * @param sa socket numerical address
+ * @param sock socket
+ * @return the peer information
+ */
+char* Socket_getaddrname(struct sockaddr* sa, int sock)
+{
+/**
+ * maximum length of the address string
+ */
+#define ADDRLEN INET6_ADDRSTRLEN+1
+/**
+ * maximum length of the port string
+ */
+#define PORTLEN 10
+ static char addr_string[ADDRLEN + PORTLEN];
+
+#if defined(WIN32) || defined(WIN64)
+ int buflen = ADDRLEN*2;
+ wchar_t buf[ADDRLEN*2];
+ if (WSAAddressToStringW(sa, sizeof(struct sockaddr_in6), NULL, buf, (LPDWORD)&buflen) == SOCKET_ERROR)
+ Socket_error("WSAAddressToString", sock);
+ else
+ wcstombs(addr_string, buf, sizeof(addr_string));
+ /* TODO: append the port information - format: [00:00:00::]:port */
+ /* strcpy(&addr_string[strlen(addr_string)], "what?"); */
+#else
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ inet_ntop(sin->sin_family, &sin->sin_addr, addr_string, ADDRLEN);
+ sprintf(&addr_string[strlen(addr_string)], ":%d", ntohs(sin->sin_port));
+#endif
+ return addr_string;
+}
+
+
+/**
+ * Get information about the other end connected to a socket
+ * @param sock the socket to inquire on
+ * @return the peer information
+ */
+char* Socket_getpeer(int sock)
+{
+ struct sockaddr_in6 sa;
+ socklen_t sal = sizeof(sa);
+ int rc;
+
+ if ((rc = getpeername(sock, (struct sockaddr*)&sa, &sal)) == SOCKET_ERROR)
+ {
+ Socket_error("getpeername", sock);
+ return "unknown";
+ }
+
+ return Socket_getaddrname((struct sockaddr*)&sa, sock);
+}
+
+
+#if defined(Socket_TEST)
+
+int main(int argc, char *argv[])
+{
+ Socket_connect("127.0.0.1", 1883);
+ Socket_connect("localhost", 1883);
+ Socket_connect("loadsadsacalhost", 1883);
+}
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/Socket.h b/tts-mqtt-test/src/paho-mqtt/Socket.h
new file mode 100644
index 0000000..c72189d
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Socket.h
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial implementation and documentation
+ * Ian Craggs - async client updates
+ *******************************************************************************/
+
+#if !defined(SOCKET_H)
+#define SOCKET_H
+
+#include <sys/types.h>
+
+#if defined(WIN32) || defined(WIN64)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define MAXHOSTNAMELEN 256
+#if !defined(SSLSOCKET_H)
+#undef EAGAIN
+#define EAGAIN WSAEWOULDBLOCK
+#undef EINTR
+#define EINTR WSAEINTR
+#undef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#undef EWOULDBLOCK
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#undef ENOTCONN
+#define ENOTCONN WSAENOTCONN
+#undef ECONNRESET
+#define ECONNRESET WSAECONNRESET
+#undef ETIMEDOUT
+#define ETIMEDOUT WAIT_TIMEOUT
+#endif
+#define ioctl ioctlsocket
+#define socklen_t int
+#else
+#define INVALID_SOCKET SOCKET_ERROR
+#include <sys/socket.h>
+#if !defined(_WRS_KERNEL)
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/uio.h>
+#else
+#include <selectLib.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define ULONG size_t
+#endif
+
+#include "../paho-mqtt/mutex_type.h" /* Needed for mutex_type */
+
+/** socket operation completed successfully */
+#define TCPSOCKET_COMPLETE 0
+#if !defined(SOCKET_ERROR)
+ /** error in socket operation */
+ #define SOCKET_ERROR -1
+#endif
+/** must be the same as SOCKETBUFFER_INTERRUPTED */
+#define TCPSOCKET_INTERRUPTED -22
+#define SSL_FATAL -3
+
+#if !defined(INET6_ADDRSTRLEN)
+#define INET6_ADDRSTRLEN 46 /** only needed for gcc/cygwin on windows */
+#endif
+
+
+#if !defined(max)
+#define max(A,B) ( (A) > (B) ? (A):(B))
+#endif
+
+#include "../paho-mqtt/LinkedList.h"
+
+/*BE
+def FD_SET
+{
+ 128 n8 "data"
+}
+
+def SOCKETS
+{
+ FD_SET "rset"
+ FD_SET "rset_saved"
+ n32 dec "maxfdp1"
+ n32 ptr INTList "clientsds"
+ n32 ptr INTItem "cur_clientsds"
+ n32 ptr INTList "connect_pending"
+ n32 ptr INTList "write_pending"
+ FD_SET "pending_wset"
+}
+BE*/
+
+
+/**
+ * Structure to hold all socket data for the module
+ */
+typedef struct
+{
+ fd_set rset, /**< socket read set (see select doc) */
+ rset_saved; /**< saved socket read set */
+ int maxfdp1; /**< max descriptor used +1 (again see select doc) */
+ List* clientsds; /**< list of client socket descriptors */
+ ListElement* cur_clientsds; /**< current client socket descriptor (iterator) */
+ List* connect_pending; /**< list of sockets for which a connect is pending */
+ List* write_pending; /**< list of sockets for which a write is pending */
+ fd_set pending_wset; /**< socket pending write set for select */
+} Sockets;
+
+
+void Socket_outInitialize(void);
+void Socket_outTerminate(void);
+int Socket_getReadySocket(int more_work, struct timeval *tp, mutex_type mutex);
+int Socket_getch(int socket, char* c);
+char *Socket_getdata(int socket, size_t bytes, size_t* actual_len);
+int Socket_putdatas(int socket, char* buf0, size_t buf0len, int count, char** buffers, size_t* buflens, int* frees);
+void Socket_close(int socket);
+int Socket_new(char* addr, int port, int* socket);
+
+int Socket_noPendingWrites(int socket);
+char* Socket_getpeer(int sock);
+
+void Socket_addPendingWrite(int socket);
+void Socket_clearPendingWrite(int socket);
+
+typedef void Socket_writeComplete(int socket, int rc);
+void Socket_setWriteCompleteCallback(Socket_writeComplete*);
+
+#endif /* SOCKET_H */
diff --git a/tts-mqtt-test/src/paho-mqtt/SocketBuffer.c b/tts-mqtt-test/src/paho-mqtt/SocketBuffer.c
new file mode 100644
index 0000000..462fc6a
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/SocketBuffer.c
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ * Ian Craggs - fix for issue #244, issue #20
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Socket buffering related functions
+ *
+ * Some other related functions are in the Socket module
+ */
+#include "../paho-mqtt/SocketBuffer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../paho-mqtt/Heap.h"
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/Messages.h"
+#include "../paho-mqtt/StackTrace.h"
+
+#if defined(WIN32) || defined(WIN64)
+#define iov_len len
+#define iov_base buf
+#endif
+
+/**
+ * Default input queue buffer
+ */
+static socket_queue* def_queue;
+
+/**
+ * List of queued input buffers
+ */
+static List* queues;
+
+/**
+ * List of queued write buffers
+ */
+static List writes;
+
+
+int socketcompare(void* a, void* b);
+void SocketBuffer_newDefQ(void);
+void SocketBuffer_freeDefQ(void);
+int pending_socketcompare(void* a, void* b);
+
+
+/**
+ * List callback function for comparing socket_queues by socket
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+int socketcompare(void* a, void* b)
+{
+ return ((socket_queue*)a)->socket == *(int*)b;
+}
+
+
+/**
+ * Create a new default queue when one has just been used.
+ */
+void SocketBuffer_newDefQ(void)
+{
+ def_queue = malloc(sizeof(socket_queue));
+ def_queue->buflen = 1000;
+ def_queue->buf = malloc(def_queue->buflen);
+ def_queue->socket = def_queue->index = 0;
+ def_queue->buflen = def_queue->datalen = 0;
+}
+
+
+/**
+ * Initialize the socketBuffer module
+ */
+void SocketBuffer_initialize(void)
+{
+ FUNC_ENTRY;
+ SocketBuffer_newDefQ();
+ queues = ListInitialize();
+ ListZero(&writes);
+ FUNC_EXIT;
+}
+
+
+/**
+ * Free the default queue memory
+ */
+void SocketBuffer_freeDefQ(void)
+{
+ free(def_queue->buf);
+ free(def_queue);
+ def_queue = NULL;
+}
+
+
+/**
+ * Terminate the socketBuffer module
+ */
+void SocketBuffer_terminate(void)
+{
+ ListElement* cur = NULL;
+ ListEmpty(&writes);
+
+ FUNC_ENTRY;
+ while (ListNextElement(queues, &cur))
+ free(((socket_queue*)(cur->content))->buf);
+ ListFree(queues);
+ SocketBuffer_freeDefQ();
+ FUNC_EXIT;
+}
+
+
+/**
+ * Cleanup any buffers for a specific socket
+ * @param socket the socket to clean up
+ */
+void SocketBuffer_cleanup(int socket)
+{
+ FUNC_ENTRY;
+ SocketBuffer_writeComplete(socket); /* clean up write buffers */
+ if (ListFindItem(queues, &socket, socketcompare))
+ {
+ free(((socket_queue*)(queues->current->content))->buf);
+ ListRemove(queues, queues->current->content);
+ }
+ if (def_queue->socket == socket)
+ {
+ def_queue->socket = def_queue->index = 0;
+ def_queue->headerlen = def_queue->datalen = 0;
+ }
+ FUNC_EXIT;
+}
+
+
+/**
+ * Get any queued data for a specific socket
+ * @param socket the socket to get queued data for
+ * @param bytes the number of bytes of data to retrieve
+ * @param actual_len the actual length returned
+ * @return the actual data
+ */
+char* SocketBuffer_getQueuedData(int socket, size_t bytes, size_t* actual_len)
+{
+ socket_queue* queue = NULL;
+
+ FUNC_ENTRY;
+ if (ListFindItem(queues, &socket, socketcompare))
+ { /* if there is queued data for this socket, add any data read to it */
+ queue = (socket_queue*)(queues->current->content);
+ *actual_len = queue->datalen;
+ }
+ else
+ {
+ *actual_len = 0;
+ queue = def_queue;
+ }
+ if (bytes > queue->buflen)
+ {
+ if (queue->datalen > 0)
+ {
+ void* newmem = malloc(bytes);
+ memcpy(newmem, queue->buf, queue->datalen);
+ free(queue->buf);
+ queue->buf = newmem;
+ }
+ else
+ queue->buf = realloc(queue->buf, bytes);
+ queue->buflen = bytes;
+ }
+
+ FUNC_EXIT;
+ return queue->buf;
+}
+
+
+/**
+ * Get any queued character for a specific socket
+ * @param socket the socket to get queued data for
+ * @param c the character returned if any
+ * @return completion code
+ */
+int SocketBuffer_getQueuedChar(int socket, char* c)
+{
+ int rc = SOCKETBUFFER_INTERRUPTED;
+
+ FUNC_ENTRY;
+ if (ListFindItem(queues, &socket, socketcompare))
+ { /* if there is queued data for this socket, read that first */
+ socket_queue* queue = (socket_queue*)(queues->current->content);
+ if (queue->index < queue->headerlen)
+ {
+ *c = queue->fixed_header[(queue->index)++];
+ Log(TRACE_MAX, -1, "index is now %d, headerlen %d", queue->index, queue->headerlen);
+ rc = SOCKETBUFFER_COMPLETE;
+ goto exit;
+ }
+ else if (queue->index > 4)
+ {
+ Log(LOG_FATAL, -1, "header is already at full length");
+ rc = SOCKET_ERROR;
+ goto exit;
+ }
+ }
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc; /* there was no queued char if rc is SOCKETBUFFER_INTERRUPTED*/
+}
+
+
+/**
+ * A socket read was interrupted so we need to queue data
+ * @param socket the socket to get queued data for
+ * @param actual_len the actual length of data that was read
+ */
+void SocketBuffer_interrupted(int socket, size_t actual_len)
+{
+ socket_queue* queue = NULL;
+
+ FUNC_ENTRY;
+ if (ListFindItem(queues, &socket, socketcompare))
+ queue = (socket_queue*)(queues->current->content);
+ else /* new saved queue */
+ {
+ queue = def_queue;
+ /* if SocketBuffer_queueChar() has not yet been called, then the socket number
+ in def_queue will not have been set. Issue #244.
+ If actual_len == 0 then we may not need to do anything - I'll leave that
+ optimization for another time. */
+ queue->socket = socket;
+ ListAppend(queues, def_queue, sizeof(socket_queue)+def_queue->buflen);
+ SocketBuffer_newDefQ();
+ }
+ queue->index = 0;
+ queue->datalen = actual_len;
+ FUNC_EXIT;
+}
+
+
+/**
+ * A socket read has now completed so we can get rid of the queue
+ * @param socket the socket for which the operation is now complete
+ * @return pointer to the default queue data
+ */
+char* SocketBuffer_complete(int socket)
+{
+ FUNC_ENTRY;
+ if (ListFindItem(queues, &socket, socketcompare))
+ {
+ socket_queue* queue = (socket_queue*)(queues->current->content);
+ SocketBuffer_freeDefQ();
+ def_queue = queue;
+ ListDetach(queues, queue);
+ }
+ def_queue->socket = def_queue->index = 0;
+ def_queue->headerlen = def_queue->datalen = 0;
+ FUNC_EXIT;
+ return def_queue->buf;
+}
+
+
+/**
+ * A socket operation had now completed so we can get rid of the queue
+ * @param socket the socket for which the operation is now complete
+ * @param c the character to queue
+ */
+void SocketBuffer_queueChar(int socket, char c)
+{
+ int error = 0;
+ socket_queue* curq = def_queue;
+
+ FUNC_ENTRY;
+ if (ListFindItem(queues, &socket, socketcompare))
+ curq = (socket_queue*)(queues->current->content);
+ else if (def_queue->socket == 0)
+ {
+ def_queue->socket = socket;
+ def_queue->index = 0;
+ def_queue->datalen = 0;
+ }
+ else if (def_queue->socket != socket)
+ {
+ Log(LOG_FATAL, -1, "attempt to reuse socket queue");
+ error = 1;
+ }
+ if (curq->index > 4)
+ {
+ Log(LOG_FATAL, -1, "socket queue fixed_header field full");
+ error = 1;
+ }
+ if (!error)
+ {
+ curq->fixed_header[(curq->index)++] = c;
+ curq->headerlen = curq->index;
+ }
+ Log(TRACE_MAX, -1, "queueChar: index is now %d, headerlen %d", curq->index, curq->headerlen);
+ FUNC_EXIT;
+}
+
+
+/**
+ * A socket write was interrupted so store the remaining data
+ * @param socket the socket for which the write was interrupted
+ * @param count the number of iovec buffers
+ * @param iovecs buffer array
+ * @param frees a set of flags indicating which of the iovecs array should be freed
+ * @param total total data length to be written
+ * @param bytes actual data length that was written
+ */
+#if defined(OPENSSL)
+void SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes)
+#else
+void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes)
+#endif
+{
+ int i = 0;
+ pending_writes* pw = NULL;
+
+ FUNC_ENTRY;
+ /* store the buffers until the whole packet is written */
+ pw = malloc(sizeof(pending_writes));
+ pw->socket = socket;
+#if defined(OPENSSL)
+ pw->ssl = ssl;
+#endif
+ pw->bytes = bytes;
+ pw->total = total;
+ pw->count = count;
+ for (i = 0; i < count; i++)
+ {
+ pw->iovecs[i] = iovecs[i];
+ pw->frees[i] = frees[i];
+ }
+ ListAppend(&writes, pw, sizeof(pw) + total);
+ FUNC_EXIT;
+}
+
+
+/**
+ * List callback function for comparing pending_writes by socket
+ * @param a first integer value
+ * @param b second integer value
+ * @return boolean indicating whether a and b are equal
+ */
+int pending_socketcompare(void* a, void* b)
+{
+ return ((pending_writes*)a)->socket == *(int*)b;
+}
+
+
+/**
+ * Get any queued write data for a specific socket
+ * @param socket the socket to get queued data for
+ * @return pointer to the queued data or NULL
+ */
+pending_writes* SocketBuffer_getWrite(int socket)
+{
+ ListElement* le = ListFindItem(&writes, &socket, pending_socketcompare);
+ return (le) ? (pending_writes*)(le->content) : NULL;
+}
+
+
+/**
+ * A socket write has now completed so we can get rid of the queue
+ * @param socket the socket for which the operation is now complete
+ * @return completion code, boolean - was the queue removed?
+ */
+int SocketBuffer_writeComplete(int socket)
+{
+ return ListRemoveItem(&writes, &socket, pending_socketcompare);
+}
+
+
+/**
+ * Update the queued write data for a socket in the case of QoS 0 messages.
+ * @param socket the socket for which the operation is now complete
+ * @param topic the topic of the QoS 0 write
+ * @param payload the payload of the QoS 0 write
+ * @return pointer to the updated queued data structure, or NULL
+ */
+pending_writes* SocketBuffer_updateWrite(int socket, char* topic, char* payload)
+{
+ pending_writes* pw = NULL;
+ ListElement* le = NULL;
+
+ FUNC_ENTRY;
+ if ((le = ListFindItem(&writes, &socket, pending_socketcompare)) != NULL)
+ {
+ pw = (pending_writes*)(le->content);
+ if (pw->count == 4)
+ {
+ pw->iovecs[2].iov_base = topic;
+ pw->iovecs[3].iov_base = payload;
+ }
+ }
+
+ FUNC_EXIT;
+ return pw;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/SocketBuffer.h b/tts-mqtt-test/src/paho-mqtt/SocketBuffer.h
new file mode 100644
index 0000000..f7702dc
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/SocketBuffer.h
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ * Ian Craggs, Allan Stockdill-Mander - SSL updates
+ *******************************************************************************/
+
+#if !defined(SOCKETBUFFER_H)
+#define SOCKETBUFFER_H
+
+#if defined(WIN32) || defined(WIN64)
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#endif
+
+#if defined(OPENSSL)
+#include <openssl/ssl.h>
+#endif
+
+#if defined(WIN32) || defined(WIN64)
+ typedef WSABUF iobuf;
+#else
+ typedef struct iovec iobuf;
+#endif
+
+typedef struct
+{
+ int socket;
+ unsigned int index;
+ size_t headerlen;
+ char fixed_header[5]; /**< header plus up to 4 length bytes */
+ size_t buflen, /**< total length of the buffer */
+ datalen; /**< current length of data in buf */
+ char* buf;
+} socket_queue;
+
+typedef struct
+{
+ int socket, count;
+ size_t total;
+#if defined(OPENSSL)
+ SSL* ssl;
+#endif
+ size_t bytes;
+ iobuf iovecs[5];
+ int frees[5];
+} pending_writes;
+
+#define SOCKETBUFFER_COMPLETE 0
+#if !defined(SOCKET_ERROR)
+ #define SOCKET_ERROR -1
+#endif
+#define SOCKETBUFFER_INTERRUPTED -22 /* must be the same value as TCPSOCKET_INTERRUPTED */
+
+void SocketBuffer_initialize(void);
+void SocketBuffer_terminate(void);
+void SocketBuffer_cleanup(int socket);
+char* SocketBuffer_getQueuedData(int socket, size_t bytes, size_t* actual_len);
+int SocketBuffer_getQueuedChar(int socket, char* c);
+void SocketBuffer_interrupted(int socket, size_t actual_len);
+char* SocketBuffer_complete(int socket);
+void SocketBuffer_queueChar(int socket, char c);
+
+#if defined(OPENSSL)
+void SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes);
+#else
+void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes);
+#endif
+pending_writes* SocketBuffer_getWrite(int socket);
+int SocketBuffer_writeComplete(int socket);
+pending_writes* SocketBuffer_updateWrite(int socket, char* topic, char* payload);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/StackTrace.c b/tts-mqtt-test/src/paho-mqtt/StackTrace.c
new file mode 100644
index 0000000..620d8da
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/StackTrace.c
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "../paho-mqtt/StackTrace.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../paho-mqtt/Clients.h"
+#include "../paho-mqtt/LinkedList.h"
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/Thread.h"
+
+#if defined(WIN32) || defined(WIN64)
+#define snprintf _snprintf
+#endif
+
+/*BE
+def STACKENTRY
+{
+ n32 ptr STRING open "name"
+ n32 dec "line"
+}
+
+defList(STACKENTRY)
+BE*/
+
+#define MAX_STACK_DEPTH 50
+#define MAX_FUNCTION_NAME_LENGTH 30
+#define MAX_THREADS 255
+
+typedef struct
+{
+ thread_id_type threadid;
+ char name[MAX_FUNCTION_NAME_LENGTH];
+ int line;
+} stackEntry;
+
+typedef struct
+{
+ thread_id_type id;
+ int maxdepth;
+ int current_depth;
+ stackEntry callstack[MAX_STACK_DEPTH];
+} threadEntry;
+
+#include "../paho-mqtt/StackTrace.h"
+
+static int thread_count = 0;
+static threadEntry threads[MAX_THREADS];
+static threadEntry *cur_thread = NULL;
+
+#if defined(WIN32) || defined(WIN64)
+mutex_type stack_mutex;
+#else
+static pthread_mutex_t stack_mutex_store = PTHREAD_MUTEX_INITIALIZER;
+static mutex_type stack_mutex = &stack_mutex_store;
+#endif
+
+
+int setStack(int create);
+
+
+int setStack(int create)
+{
+ int i = -1;
+ thread_id_type curid = Thread_getid();
+
+ cur_thread = NULL;
+ for (i = 0; i < MAX_THREADS && i < thread_count; ++i)
+ {
+ if (threads[i].id == curid)
+ {
+ cur_thread = &threads[i];
+ break;
+ }
+ }
+
+ if (cur_thread == NULL && create && thread_count < MAX_THREADS)
+ {
+ cur_thread = &threads[thread_count];
+ cur_thread->id = curid;
+ cur_thread->maxdepth = 0;
+ cur_thread->current_depth = 0;
+ ++thread_count;
+ }
+ return cur_thread != NULL; /* good == 1 */
+}
+
+void StackTrace_entry(const char* name, int line, enum LOG_LEVELS trace_level)
+{
+ Thread_lock_mutex(stack_mutex);
+ if (!setStack(1))
+ goto exit;
+ if (trace_level != -1)
+ Log_stackTrace(trace_level, 9, (int)cur_thread->id, cur_thread->current_depth, name, line, NULL);
+ strncpy(cur_thread->callstack[cur_thread->current_depth].name, name, sizeof(cur_thread->callstack[0].name)-1);
+ cur_thread->callstack[(cur_thread->current_depth)++].line = line;
+ if (cur_thread->current_depth > cur_thread->maxdepth)
+ cur_thread->maxdepth = cur_thread->current_depth;
+ if (cur_thread->current_depth >= MAX_STACK_DEPTH)
+ Log(LOG_FATAL, -1, "Max stack depth exceeded");
+exit:
+ Thread_unlock_mutex(stack_mutex);
+}
+
+
+void StackTrace_exit(const char* name, int line, void* rc, enum LOG_LEVELS trace_level)
+{
+ Thread_lock_mutex(stack_mutex);
+ if (!setStack(0))
+ goto exit;
+ if (--(cur_thread->current_depth) < 0)
+ Log(LOG_FATAL, -1, "Minimum stack depth exceeded for thread %lu", cur_thread->id);
+ if (strncmp(cur_thread->callstack[cur_thread->current_depth].name, name, sizeof(cur_thread->callstack[0].name)-1) != 0)
+ Log(LOG_FATAL, -1, "Stack mismatch. Entry:%s Exit:%s\n", cur_thread->callstack[cur_thread->current_depth].name, name);
+ if (trace_level != -1)
+ {
+ if (rc == NULL)
+ Log_stackTrace(trace_level, 10, (int)cur_thread->id, cur_thread->current_depth, name, line, NULL);
+ else
+ Log_stackTrace(trace_level, 11, (int)cur_thread->id, cur_thread->current_depth, name, line, (int*)rc);
+ }
+exit:
+ Thread_unlock_mutex(stack_mutex);
+}
+
+
+void StackTrace_printStack(FILE* dest)
+{
+ FILE* file = stdout;
+ int t = 0;
+
+ if (dest)
+ file = dest;
+ for (t = 0; t < thread_count; ++t)
+ {
+ threadEntry *cur_thread = &threads[t];
+
+ if (cur_thread->id > 0)
+ {
+ int i = cur_thread->current_depth - 1;
+
+ fprintf(file, "=========== Start of stack trace for thread %lu ==========\n", (unsigned long)cur_thread->id);
+ if (i >= 0)
+ {
+ fprintf(file, "%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
+ while (--i >= 0)
+ fprintf(file, " at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
+ }
+ fprintf(file, "=========== End of stack trace for thread %lu ==========\n\n", (unsigned long)cur_thread->id);
+ }
+ }
+ if (file != stdout && file != stderr && file != NULL)
+ fclose(file);
+}
+
+
+char* StackTrace_get(thread_id_type threadid, char* buf, int bufsize)
+{
+ int t = 0;
+
+ if (bufsize < 100)
+ goto exit;
+ buf[0] = '\0';
+ for (t = 0; t < thread_count; ++t)
+ {
+ threadEntry *cur_thread = &threads[t];
+
+ if (cur_thread->id == threadid)
+ {
+ int i = cur_thread->current_depth - 1;
+ int curpos = 0;
+
+ if (i >= 0)
+ {
+ curpos += snprintf(&buf[curpos], bufsize - curpos -1,
+ "%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
+ while (--i >= 0)
+ curpos += snprintf(&buf[curpos], bufsize - curpos -1,
+ " at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
+ if (buf[--curpos] == '\n')
+ buf[curpos] = '\0';
+ }
+ break;
+ }
+ }
+exit:
+ return buf;
+}
diff --git a/tts-mqtt-test/src/paho-mqtt/StackTrace.h b/tts-mqtt-test/src/paho-mqtt/StackTrace.h
new file mode 100644
index 0000000..9ff1d72
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/StackTrace.h
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#ifndef STACKTRACE_H_
+#define STACKTRACE_H_
+
+#include <stdio.h>
+
+#include "../paho-mqtt/Log.h"
+#include "../paho-mqtt/Thread.h"
+
+#if defined(NOSTACKTRACE)
+#define FUNC_ENTRY
+#define FUNC_ENTRY_NOLOG
+#define FUNC_ENTRY_MED
+#define FUNC_ENTRY_MAX
+#define FUNC_EXIT
+#define FUNC_EXIT_NOLOG
+#define FUNC_EXIT_MED
+#define FUNC_EXIT_MAX
+#define FUNC_EXIT_RC(x)
+#define FUNC_EXIT_MED_RC(x)
+#define FUNC_EXIT_MAX_RC(x)
+#else
+#if defined(WIN32) || defined(WIN64)
+#define inline __inline
+#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
+#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
+#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
+#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
+#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
+#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
+#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
+#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
+#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
+#else
+#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
+#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
+#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
+#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
+#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
+#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
+#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
+#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
+#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
+#endif
+#endif
+
+void StackTrace_entry(const char* name, int line, enum LOG_LEVELS trace);
+void StackTrace_exit(const char* name, int line, void* return_value, enum LOG_LEVELS trace);
+
+void StackTrace_printStack(FILE* dest);
+char* StackTrace_get(thread_id_type, char* buf, int bufsize);
+
+#endif /* STACKTRACE_H_ */
diff --git a/tts-mqtt-test/src/paho-mqtt/Thread.c b/tts-mqtt-test/src/paho-mqtt/Thread.c
new file mode 100644
index 0000000..3fd78dc
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Thread.c
@@ -0,0 +1,462 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial implementation
+ * Ian Craggs, Allan Stockdill-Mander - async client updates
+ * Ian Craggs - bug #415042 - start Linux thread as disconnected
+ * Ian Craggs - fix for bug #420851
+ * Ian Craggs - change MacOS semaphore implementation
+ *******************************************************************************/
+
+/**
+ * @file
+ * \brief Threading related functions
+ *
+ * Used to create platform independent threading functions
+ */
+
+
+#include "../paho-mqtt/Thread.h"
+#if defined(THREAD_UNIT_TESTS)
+#define NOSTACKTRACE
+#endif
+#include "../paho-mqtt/StackTrace.h"
+
+#undef malloc
+#undef realloc
+#undef free
+
+#if !defined(WIN32) && !defined(WIN64)
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <limits.h>
+#endif
+#include <stdlib.h>
+
+#include "../paho-mqtt/OsWrapper.h"
+
+/**
+ * Start a new thread
+ * @param fn the function to run, must be of the correct signature
+ * @param parameter pointer to the function parameter, can be NULL
+ * @return the new thread
+ */
+thread_type Thread_start(thread_fn fn, void* parameter)
+{
+#if defined(WIN32) || defined(WIN64)
+ thread_type thread = NULL;
+#else
+ thread_type thread = 0;
+ pthread_attr_t attr;
+#endif
+
+ FUNC_ENTRY;
+#if defined(WIN32) || defined(WIN64)
+ thread = CreateThread(NULL, 0, fn, parameter, 0, NULL);
+#else
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (pthread_create(&thread, &attr, fn, parameter) != 0)
+ thread = 0;
+ pthread_attr_destroy(&attr);
+#endif
+ FUNC_EXIT;
+ return thread;
+}
+
+
+/**
+ * Create a new mutex
+ * @return the new mutex
+ */
+mutex_type Thread_create_mutex(void)
+{
+ mutex_type mutex = NULL;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ #if defined(WIN32) || defined(WIN64)
+ mutex = CreateMutex(NULL, 0, NULL);
+ if (mutex == NULL)
+ rc = GetLastError();
+ #else
+ mutex = malloc(sizeof(pthread_mutex_t));
+ rc = pthread_mutex_init(mutex, NULL);
+ #endif
+ FUNC_EXIT_RC(rc);
+ return mutex;
+}
+
+
+/**
+ * Lock a mutex which has alrea
+ * @return completion code, 0 is success
+ */
+int Thread_lock_mutex(mutex_type mutex)
+{
+ int rc = -1;
+
+ /* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
+ #if defined(WIN32) || defined(WIN64)
+ /* WaitForSingleObject returns WAIT_OBJECT_0 (0), on success */
+ rc = WaitForSingleObject(mutex, INFINITE);
+ #else
+ rc = pthread_mutex_lock(mutex);
+ #endif
+
+ return rc;
+}
+
+
+/**
+ * Unlock a mutex which has already been locked
+ * @param mutex the mutex
+ * @return completion code, 0 is success
+ */
+int Thread_unlock_mutex(mutex_type mutex)
+{
+ int rc = -1;
+
+ /* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
+ #if defined(WIN32) || defined(WIN64)
+ /* if ReleaseMutex fails, the return value is 0 */
+ if (ReleaseMutex(mutex) == 0)
+ rc = GetLastError();
+ else
+ rc = 0;
+ #else
+ rc = pthread_mutex_unlock(mutex);
+ #endif
+
+ return rc;
+}
+
+
+/**
+ * Destroy a mutex which has already been created
+ * @param mutex the mutex
+ */
+void Thread_destroy_mutex(mutex_type mutex)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ #if defined(WIN32) || defined(WIN64)
+ rc = CloseHandle(mutex);
+ #else
+ rc = pthread_mutex_destroy(mutex);
+ free(mutex);
+ #endif
+ FUNC_EXIT_RC(rc);
+}
+
+
+/**
+ * Get the thread id of the thread from which this function is called
+ * @return thread id, type varying according to OS
+ */
+thread_id_type Thread_getid(void)
+{
+ #if defined(WIN32) || defined(WIN64)
+ return GetCurrentThreadId();
+ #else
+ return pthread_self();
+ #endif
+}
+
+
+/**
+ * Create a new semaphore
+ * @return the new condition variable
+ */
+sem_type Thread_create_sem(void)
+{
+ sem_type sem = NULL;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ #if defined(WIN32) || defined(WIN64)
+ sem = CreateEvent(
+ NULL, /* default security attributes */
+ FALSE, /* manual-reset event? */
+ FALSE, /* initial state is nonsignaled */
+ NULL /* object name */
+ );
+ #elif defined(OSX)
+ sem = dispatch_semaphore_create(0L);
+ rc = (sem == NULL) ? -1 : 0;
+ #else
+ sem = malloc(sizeof(sem_t));
+ rc = sem_init(sem, 0, 0);
+ #endif
+ FUNC_EXIT_RC(rc);
+ return sem;
+}
+
+
+/**
+ * Wait for a semaphore to be posted, or timeout.
+ * @param sem the semaphore
+ * @param timeout the maximum time to wait, in milliseconds
+ * @return completion code
+ */
+int Thread_wait_sem(sem_type sem, int timeout)
+{
+/* sem_timedwait is the obvious call to use, but seemed not to work on the Viper,
+ * so I've used trywait in a loop instead. Ian Craggs 23/7/2010
+ */
+ int rc = -1;
+#if !defined(WIN32) && !defined(WIN64) && !defined(OSX)
+#define USE_TRYWAIT
+#if defined(USE_TRYWAIT)
+ int i = 0;
+ int interval = 10000; /* 10000 microseconds: 10 milliseconds */
+ int count = (1000 * timeout) / interval; /* how many intervals in timeout period */
+#else
+ struct timespec ts;
+#endif
+#endif
+
+ FUNC_ENTRY;
+ #if defined(WIN32) || defined(WIN64)
+ rc = WaitForSingleObject(sem, timeout < 0 ? 0 : timeout);
+ #elif defined(OSX)
+ rc = (int)dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout*1000000L));
+ #elif defined(USE_TRYWAIT)
+ while (++i < count && (rc = sem_trywait(sem)) != 0)
+ {
+ if (rc == -1 && ((rc = errno) != EAGAIN))
+ {
+ rc = 0;
+ break;
+ }
+ usleep(interval); /* microseconds - .1 of a second */
+ }
+ #else
+ if (clock_gettime(CLOCK_REALTIME, &ts) != -1)
+ {
+ ts.tv_sec += timeout;
+ rc = sem_timedwait(sem, &ts);
+ }
+ #endif
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Check to see if a semaphore has been posted, without waiting.
+ * @param sem the semaphore
+ * @return 0 (false) or 1 (true)
+ */
+int Thread_check_sem(sem_type sem)
+{
+#if defined(WIN32) || defined(WIN64)
+ return WaitForSingleObject(sem, 0) == WAIT_OBJECT_0;
+#elif defined(OSX)
+ return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
+#else
+ int semval = -1;
+ sem_getvalue(sem, &semval);
+ return semval > 0;
+#endif
+}
+
+
+/**
+ * Post a semaphore
+ * @param sem the semaphore
+ * @return completion code
+ */
+int Thread_post_sem(sem_type sem)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ #if defined(WIN32) || defined(WIN64)
+ if (SetEvent(sem) == 0)
+ rc = GetLastError();
+ #elif defined(OSX)
+ rc = (int)dispatch_semaphore_signal(sem);
+ #else
+ if (sem_post(sem) == -1)
+ rc = errno;
+ #endif
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Destroy a semaphore which has already been created
+ * @param sem the semaphore
+ */
+int Thread_destroy_sem(sem_type sem)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ #if defined(WIN32) || defined(WIN64)
+ rc = CloseHandle(sem);
+ #elif defined(OSX)
+ dispatch_release(sem);
+ #else
+ rc = sem_destroy(sem);
+ free(sem);
+ #endif
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+#if !defined(WIN32) && !defined(WIN64)
+/**
+ * Create a new condition variable
+ * @return the condition variable struct
+ */
+cond_type Thread_create_cond(void)
+{
+ cond_type condvar = NULL;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ condvar = malloc(sizeof(cond_type_struct));
+ rc = pthread_cond_init(&condvar->cond, NULL);
+ rc = pthread_mutex_init(&condvar->mutex, NULL);
+
+ FUNC_EXIT_RC(rc);
+ return condvar;
+}
+
+/**
+ * Signal a condition variable
+ * @return completion code
+ */
+int Thread_signal_cond(cond_type condvar)
+{
+ int rc = 0;
+
+ pthread_mutex_lock(&condvar->mutex);
+ rc = pthread_cond_signal(&condvar->cond);
+ pthread_mutex_unlock(&condvar->mutex);
+
+ return rc;
+}
+
+/**
+ * Wait with a timeout (seconds) for condition variable
+ * @return completion code
+ */
+int Thread_wait_cond(cond_type condvar, int timeout)
+{
+ FUNC_ENTRY;
+ int rc = 0;
+ struct timespec cond_timeout;
+ struct timeval cur_time;
+
+ gettimeofday(&cur_time, NULL);
+
+ cond_timeout.tv_sec = cur_time.tv_sec + timeout;
+ cond_timeout.tv_nsec = cur_time.tv_usec * 1000;
+
+ pthread_mutex_lock(&condvar->mutex);
+ rc = pthread_cond_timedwait(&condvar->cond, &condvar->mutex, &cond_timeout);
+ pthread_mutex_unlock(&condvar->mutex);
+
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+/**
+ * Destroy a condition variable
+ * @return completion code
+ */
+int Thread_destroy_cond(cond_type condvar)
+{
+ int rc = 0;
+
+ rc = pthread_mutex_destroy(&condvar->mutex);
+ rc = pthread_cond_destroy(&condvar->cond);
+ free(condvar);
+
+ return rc;
+}
+#endif
+
+
+#if defined(THREAD_UNIT_TESTS)
+
+#include <stdio.h>
+
+thread_return_type secondary(void* n)
+{
+ int rc = 0;
+
+ /*
+ cond_type cond = n;
+
+ printf("Secondary thread about to wait\n");
+ rc = Thread_wait_cond(cond);
+ printf("Secondary thread returned from wait %d\n", rc);*/
+
+ sem_type sem = n;
+
+ printf("Secondary thread about to wait\n");
+ rc = Thread_wait_sem(sem);
+ printf("Secondary thread returned from wait %d\n", rc);
+
+ printf("Secondary thread about to wait\n");
+ rc = Thread_wait_sem(sem);
+ printf("Secondary thread returned from wait %d\n", rc);
+ printf("Secondary check sem %d\n", Thread_check_sem(sem));
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int rc = 0;
+
+ sem_type sem = Thread_create_sem();
+
+ printf("check sem %d\n", Thread_check_sem(sem));
+
+ printf("post secondary\n");
+ rc = Thread_post_sem(sem);
+ printf("posted secondary %d\n", rc);
+
+ printf("check sem %d\n", Thread_check_sem(sem));
+
+ printf("Starting secondary thread\n");
+ Thread_start(secondary, (void*)sem);
+
+ sleep(3);
+ printf("check sem %d\n", Thread_check_sem(sem));
+
+ printf("post secondary\n");
+ rc = Thread_post_sem(sem);
+ printf("posted secondary %d\n", rc);
+
+ sleep(3);
+
+ printf("Main thread ending\n");
+}
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/Thread.h b/tts-mqtt-test/src/paho-mqtt/Thread.h
new file mode 100644
index 0000000..b42c075
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Thread.h
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial implementation
+ * Ian Craggs, Allan Stockdill-Mander - async client updates
+ * Ian Craggs - fix for bug #420851
+ * Ian Craggs - change MacOS semaphore implementation
+ *******************************************************************************/
+#include "../paho-mqtt/MQTTClient.h"
+
+#if !defined(THREAD_H)
+#define THREAD_H
+
+#include "../paho-mqtt/mutex_type.h" /* Needed for mutex_type */
+
+#if defined(WIN32) || defined(WIN64)
+ #include <windows.h>
+ #define thread_type HANDLE
+ #define thread_id_type DWORD
+ #define thread_return_type DWORD
+ #define thread_fn LPTHREAD_START_ROUTINE
+ #define cond_type HANDLE
+ #define sem_type HANDLE
+#else
+ #include <pthread.h>
+
+ #define thread_type pthread_t
+ #define thread_id_type pthread_t
+ #define thread_return_type void*
+ typedef thread_return_type (*thread_fn)(void*);
+ typedef struct { pthread_cond_t cond; pthread_mutex_t mutex; } cond_type_struct;
+ typedef cond_type_struct *cond_type;
+ #if defined(OSX)
+ #include <dispatch/dispatch.h>
+ typedef dispatch_semaphore_t sem_type;
+ #else
+ #include <semaphore.h>
+ typedef sem_t *sem_type;
+ #endif
+
+ cond_type Thread_create_cond(void);
+ int Thread_signal_cond(cond_type);
+ int Thread_wait_cond(cond_type condvar, int timeout);
+ int Thread_destroy_cond(cond_type);
+#endif
+
+DLLExport thread_type Thread_start(thread_fn, void*);
+
+DLLExport mutex_type Thread_create_mutex();
+DLLExport int Thread_lock_mutex(mutex_type);
+DLLExport int Thread_unlock_mutex(mutex_type);
+void Thread_destroy_mutex(mutex_type);
+
+DLLExport thread_id_type Thread_getid();
+
+sem_type Thread_create_sem(void);
+int Thread_wait_sem(sem_type sem, int timeout);
+int Thread_check_sem(sem_type sem);
+int Thread_post_sem(sem_type sem);
+int Thread_destroy_sem(sem_type sem);
+
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/Tree.c b/tts-mqtt-test/src/paho-mqtt/Tree.c
new file mode 100644
index 0000000..ba479c0
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Tree.c
@@ -0,0 +1,726 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial implementation and documentation
+ *******************************************************************************/
+
+/** @file
+ * \brief functions which apply to tree structures.
+ *
+ * These trees can hold data of any sort, pointed to by the content pointer of the
+ * Node structure.
+ * */
+
+#define NO_HEAP_TRACKING 1
+
+#include "../paho-mqtt/Tree.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../paho-mqtt/Heap.h"
+
+
+int isRed(Node* aNode);
+int isBlack(Node* aNode);
+/*int TreeWalk(Node* curnode, int depth);*/
+/*int TreeMaxDepth(Tree *aTree);*/
+void TreeRotate(Tree* aTree, Node* curnode, int direction, int index);
+Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index);
+void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index);
+void* TreeAddByIndex(Tree* aTree, void* content, size_t size, int index);
+Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value);
+Node* TreeFindContentIndex(Tree* aTree, void* key, int index);
+Node* TreeMinimum(Node* curnode);
+Node* TreeSuccessor(Node* curnode);
+Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index);
+Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index);
+void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index);
+void* TreeRemoveIndex(Tree* aTree, void* content, int index);
+
+
+void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int))
+{
+ memset(aTree, '\0', sizeof(Tree));
+ aTree->heap_tracking = 1;
+ aTree->index[0].compare = compare;
+ aTree->indexes = 1;
+}
+
+/**
+ * Allocates and initializes a new tree structure.
+ * @return a pointer to the new tree structure
+ */
+Tree* TreeInitialize(int(*compare)(void*, void*, int))
+{
+#if defined(UNIT_TESTS)
+ Tree* newt = malloc(sizeof(Tree));
+#else
+ Tree* newt = mymalloc(__FILE__, __LINE__, sizeof(Tree));
+#endif
+ TreeInitializeNoMalloc(newt, compare);
+ return newt;
+}
+
+
+void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int))
+{
+ aTree->index[aTree->indexes].compare = compare;
+ ++(aTree->indexes);
+}
+
+
+void TreeFree(Tree* aTree)
+{
+#if defined(UNIT_TESTS)
+ free(aTree);
+#else
+ (aTree->heap_tracking) ? myfree(__FILE__, __LINE__, aTree) : free(aTree);
+#endif
+}
+
+
+#define LEFT 0
+#define RIGHT 1
+#if !defined(max)
+#define max(a, b) (a > b) ? a : b;
+#endif
+
+
+
+int isRed(Node* aNode)
+{
+ return (aNode != NULL) && (aNode->red);
+}
+
+
+int isBlack(Node* aNode)
+{
+ return (aNode == NULL) || (aNode->red == 0);
+}
+
+#if 0
+int TreeWalk(Node* curnode, int depth)
+{
+ if (curnode)
+ {
+ int left = TreeWalk(curnode->child[LEFT], depth+1);
+ int right = TreeWalk(curnode->child[RIGHT], depth+1);
+ depth = max(left, right);
+ if (curnode->red)
+ {
+ /*if (isRed(curnode->child[LEFT]) || isRed(curnode->child[RIGHT]))
+ {
+ printf("red/black tree violation %p\n", curnode->content);
+ exit(-99);
+ }*/;
+ }
+ }
+ return depth;
+}
+
+
+int TreeMaxDepth(Tree *aTree)
+{
+ int rc = TreeWalk(aTree->index[0].root, 0);
+ /*if (aTree->root->red)
+ {
+ printf("root node should not be red %p\n", aTree->root->content);
+ exit(-99);
+ }*/
+ return rc;
+}
+#endif
+
+void TreeRotate(Tree* aTree, Node* curnode, int direction, int index)
+{
+ Node* other = curnode->child[!direction];
+
+ curnode->child[!direction] = other->child[direction];
+ if (other->child[direction] != NULL)
+ other->child[direction]->parent = curnode;
+ other->parent = curnode->parent;
+ if (curnode->parent == NULL)
+ aTree->index[index].root = other;
+ else if (curnode == curnode->parent->child[direction])
+ curnode->parent->child[direction] = other;
+ else
+ curnode->parent->child[!direction] = other;
+ other->child[direction] = curnode;
+ curnode->parent = other;
+}
+
+
+Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index)
+{
+ Node* uncle = curnode->parent->parent->child[which];
+
+ if (isRed(uncle))
+ {
+ curnode->parent->red = uncle->red = 0;
+ curnode = curnode->parent->parent;
+ curnode->red = 1;
+ }
+ else
+ {
+ if (curnode == curnode->parent->child[which])
+ {
+ curnode = curnode->parent;
+ TreeRotate(aTree, curnode, !which, index);
+ }
+ curnode->parent->red = 0;
+ curnode->parent->parent->red = 1;
+ TreeRotate(aTree, curnode->parent->parent, which, index);
+ }
+ return curnode;
+}
+
+
+void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index)
+{
+ while (curnode && isRed(curnode->parent) && curnode->parent->parent)
+ {
+ if (curnode->parent == curnode->parent->parent->child[LEFT])
+ curnode = TreeBAASub(aTree, curnode, RIGHT, index);
+ else
+ curnode = TreeBAASub(aTree, curnode, LEFT, index);
+ }
+ aTree->index[index].root->red = 0;
+}
+
+
+/**
+ * Add an item to a tree
+ * @param aTree the list to which the item is to be added
+ * @param content the list item content itself
+ * @param size the size of the element
+ */
+void* TreeAddByIndex(Tree* aTree, void* content, size_t size, int index)
+{
+ Node* curparent = NULL;
+ Node* curnode = aTree->index[index].root;
+ Node* newel = NULL;
+ int left = 0;
+ int result = 1;
+ void* rc = NULL;
+
+ while (curnode)
+ {
+ result = aTree->index[index].compare(curnode->content, content, 1);
+ left = (result > 0);
+ if (result == 0)
+ break;
+ else
+ {
+ curparent = curnode;
+ curnode = curnode->child[left];
+ }
+ }
+
+ if (result == 0)
+ {
+ if (aTree->allow_duplicates)
+ goto exit; /* exit(-99); */
+ else
+ {
+ newel = curnode;
+ rc = newel->content;
+ if (index == 0)
+ aTree->size += (size - curnode->size);
+ }
+ }
+ else
+ {
+ #if defined(UNIT_TESTS)
+ newel = malloc(sizeof(Node));
+ #else
+ newel = (aTree->heap_tracking) ? mymalloc(__FILE__, __LINE__, sizeof(Node)) : malloc(sizeof(Node));
+ #endif
+ memset(newel, '\0', sizeof(Node));
+ if (curparent)
+ curparent->child[left] = newel;
+ else
+ aTree->index[index].root = newel;
+ newel->parent = curparent;
+ newel->red = 1;
+ if (index == 0)
+ {
+ ++(aTree->count);
+ aTree->size += size;
+ }
+ }
+ newel->content = content;
+ newel->size = size;
+ TreeBalanceAfterAdd(aTree, newel, index);
+exit:
+ return rc;
+}
+
+
+void* TreeAdd(Tree* aTree, void* content, size_t size)
+{
+ void* rc = NULL;
+ int i;
+
+ for (i = 0; i < aTree->indexes; ++i)
+ rc = TreeAddByIndex(aTree, content, size, i);
+
+ return rc;
+}
+
+
+Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value)
+{
+ int result = 0;
+ Node* curnode = aTree->index[index].root;
+
+ while (curnode)
+ {
+ result = aTree->index[index].compare(curnode->content, key, value);
+ if (result == 0)
+ break;
+ else
+ curnode = curnode->child[result > 0];
+ }
+ return curnode;
+}
+
+
+Node* TreeFindIndex(Tree* aTree, void* key, int index)
+{
+ return TreeFindIndex1(aTree, key, index, 0);
+}
+
+
+Node* TreeFindContentIndex(Tree* aTree, void* key, int index)
+{
+ return TreeFindIndex1(aTree, key, index, 1);
+}
+
+
+Node* TreeFind(Tree* aTree, void* key)
+{
+ return TreeFindIndex(aTree, key, 0);
+}
+
+
+Node* TreeMinimum(Node* curnode)
+{
+ if (curnode)
+ while (curnode->child[LEFT])
+ curnode = curnode->child[LEFT];
+ return curnode;
+}
+
+
+Node* TreeSuccessor(Node* curnode)
+{
+ if (curnode->child[RIGHT])
+ curnode = TreeMinimum(curnode->child[RIGHT]);
+ else
+ {
+ Node* curparent = curnode->parent;
+ while (curparent && curnode == curparent->child[RIGHT])
+ {
+ curnode = curparent;
+ curparent = curparent->parent;
+ }
+ curnode = curparent;
+ }
+ return curnode;
+}
+
+
+Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index)
+{
+ if (curnode == NULL)
+ curnode = TreeMinimum(aTree->index[index].root);
+ else
+ curnode = TreeSuccessor(curnode);
+ return curnode;
+}
+
+
+Node* TreeNextElement(Tree* aTree, Node* curnode)
+{
+ return TreeNextElementIndex(aTree, curnode, 0);
+}
+
+
+Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index)
+{
+ Node* sibling = curnode->parent->child[which];
+
+ if (isRed(sibling))
+ {
+ sibling->red = 0;
+ curnode->parent->red = 1;
+ TreeRotate(aTree, curnode->parent, !which, index);
+ sibling = curnode->parent->child[which];
+ }
+ if (!sibling)
+ curnode = curnode->parent;
+ else if (isBlack(sibling->child[!which]) && isBlack(sibling->child[which]))
+ {
+ sibling->red = 1;
+ curnode = curnode->parent;
+ }
+ else
+ {
+ if (isBlack(sibling->child[which]))
+ {
+ sibling->child[!which]->red = 0;
+ sibling->red = 1;
+ TreeRotate(aTree, sibling, which, index);
+ sibling = curnode->parent->child[which];
+ }
+ sibling->red = curnode->parent->red;
+ curnode->parent->red = 0;
+ sibling->child[which]->red = 0;
+ TreeRotate(aTree, curnode->parent, !which, index);
+ curnode = aTree->index[index].root;
+ }
+ return curnode;
+}
+
+
+void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index)
+{
+ while (curnode != aTree->index[index].root && isBlack(curnode))
+ {
+ /* curnode->content == NULL must equal curnode == NULL */
+ if (((curnode->content) ? curnode : NULL) == curnode->parent->child[LEFT])
+ curnode = TreeBARSub(aTree, curnode, RIGHT, index);
+ else
+ curnode = TreeBARSub(aTree, curnode, LEFT, index);
+ }
+ curnode->red = 0;
+}
+
+
+/**
+ * Remove an item from a tree
+ * @param aTree the list to which the item is to be added
+ * @param curnode the list item content itself
+ */
+void* TreeRemoveNodeIndex(Tree* aTree, Node* curnode, int index)
+{
+ Node* redundant = curnode;
+ Node* curchild = NULL;
+ size_t size = curnode->size;
+ void* content = curnode->content;
+
+ /* if the node to remove has 0 or 1 children, it can be removed without involving another node */
+ if (curnode->child[LEFT] && curnode->child[RIGHT]) /* 2 children */
+ redundant = TreeSuccessor(curnode); /* now redundant must have at most one child */
+
+ curchild = redundant->child[(redundant->child[LEFT] != NULL) ? LEFT : RIGHT];
+ if (curchild) /* we could have no children at all */
+ curchild->parent = redundant->parent;
+
+ if (redundant->parent == NULL)
+ aTree->index[index].root = curchild;
+ else
+ {
+ if (redundant == redundant->parent->child[LEFT])
+ redundant->parent->child[LEFT] = curchild;
+ else
+ redundant->parent->child[RIGHT] = curchild;
+ }
+
+ if (redundant != curnode)
+ {
+ curnode->content = redundant->content;
+ curnode->size = redundant->size;
+ }
+
+ if (isBlack(redundant))
+ {
+ if (curchild == NULL)
+ {
+ if (redundant->parent)
+ {
+ Node temp;
+ memset(&temp, '\0', sizeof(Node));
+ temp.parent = (redundant) ? redundant->parent : NULL;
+ temp.red = 0;
+ TreeBalanceAfterRemove(aTree, &temp, index);
+ }
+ }
+ else
+ TreeBalanceAfterRemove(aTree, curchild, index);
+ }
+
+#if defined(UNIT_TESTS)
+ free(redundant);
+#else
+ (aTree->heap_tracking) ? myfree(__FILE__, __LINE__, redundant) : free(redundant);
+#endif
+ if (index == 0)
+ {
+ aTree->size -= size;
+ --(aTree->count);
+ }
+ return content;
+}
+
+
+/**
+ * Remove an item from a tree
+ * @param aTree the list to which the item is to be added
+ * @param curnode the list item content itself
+ */
+void* TreeRemoveIndex(Tree* aTree, void* content, int index)
+{
+ Node* curnode = TreeFindContentIndex(aTree, content, index);
+
+ if (curnode == NULL)
+ return NULL;
+
+ return TreeRemoveNodeIndex(aTree, curnode, index);
+}
+
+
+void* TreeRemove(Tree* aTree, void* content)
+{
+ int i;
+ void* rc = NULL;
+
+ for (i = 0; i < aTree->indexes; ++i)
+ rc = TreeRemoveIndex(aTree, content, i);
+
+ return rc;
+}
+
+
+void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index)
+{
+ Node* curnode = TreeFindIndex(aTree, key, index);
+ void* content = NULL;
+ int i;
+
+ if (curnode == NULL)
+ return NULL;
+
+ content = TreeRemoveNodeIndex(aTree, curnode, index);
+ for (i = 0; i < aTree->indexes; ++i)
+ {
+ if (i != index)
+ content = TreeRemoveIndex(aTree, content, i);
+ }
+ return content;
+}
+
+
+void* TreeRemoveKey(Tree* aTree, void* key)
+{
+ return TreeRemoveKeyIndex(aTree, key, 0);
+}
+
+
+int TreeIntCompare(void* a, void* b, int content)
+{
+ int i = *((int*)a);
+ int j = *((int*)b);
+
+ /* printf("comparing %d %d\n", *((int*)a), *((int*)b)); */
+ return (i > j) ? -1 : (i == j) ? 0 : 1;
+}
+
+
+int TreePtrCompare(void* a, void* b, int content)
+{
+ return (a > b) ? -1 : (a == b) ? 0 : 1;
+}
+
+
+int TreeStringCompare(void* a, void* b, int content)
+{
+ return strcmp((char*)a, (char*)b);
+}
+
+
+#if defined(UNIT_TESTS)
+
+int check(Tree *t)
+{
+ Node* curnode = NULL;
+ int rc = 0;
+
+ curnode = TreeNextElement(t, curnode);
+ while (curnode)
+ {
+ Node* prevnode = curnode;
+
+ curnode = TreeNextElement(t, curnode);
+
+ if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
+ {
+ printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
+ rc = 99;
+ }
+ }
+ return rc;
+}
+
+
+int traverse(Tree *t, int lookfor)
+{
+ Node* curnode = NULL;
+ int rc = 0;
+
+ printf("Traversing\n");
+ curnode = TreeNextElement(t, curnode);
+ /* printf("content int %d\n", *(int*)(curnode->content)); */
+ while (curnode)
+ {
+ Node* prevnode = curnode;
+
+ curnode = TreeNextElement(t, curnode);
+ /* if (curnode)
+ printf("content int %d\n", *(int*)(curnode->content)); */
+ if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
+ {
+ printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
+ }
+ if (curnode && (lookfor == *(int*)(curnode->content)))
+ printf("missing item %d actually found\n", lookfor);
+ }
+ printf("End traverse %d\n", rc);
+ return rc;
+}
+
+
+int test(int limit)
+{
+ int i, *ip, *todelete;
+ Node* current = NULL;
+ Tree* t = TreeInitialize(TreeIntCompare);
+ int rc = 0;
+
+ printf("Tree initialized\n");
+
+ srand(time(NULL));
+
+ ip = malloc(sizeof(int));
+ *ip = 2;
+ TreeAdd(t, (void*)ip, sizeof(int));
+
+ check(t);
+
+ i = 2;
+ void* result = TreeRemove(t, (void*)&i);
+ if (result)
+ free(result);
+
+ int actual[limit];
+ for (i = 0; i < limit; i++)
+ {
+ void* replaced = NULL;
+
+ ip = malloc(sizeof(int));
+ *ip = rand();
+ replaced = TreeAdd(t, (void*)ip, sizeof(int));
+ if (replaced) /* duplicate */
+ {
+ free(replaced);
+ actual[i] = -1;
+ }
+ else
+ actual[i] = *ip;
+ if (i==5)
+ todelete = ip;
+ printf("Tree element added %d\n", *ip);
+ if (1 % 1000 == 0)
+ {
+ rc = check(t);
+ printf("%d elements, check result %d\n", i+1, rc);
+ if (rc != 0)
+ return 88;
+ }
+ }
+
+ check(t);
+
+ for (i = 0; i < limit; i++)
+ {
+ int parm = actual[i];
+
+ if (parm == -1)
+ continue;
+
+ Node* found = TreeFind(t, (void*)&parm);
+ if (found)
+ printf("Tree find %d %d\n", parm, *(int*)(found->content));
+ else
+ {
+ printf("%d not found\n", parm);
+ traverse(t, parm);
+ return -2;
+ }
+ }
+
+ check(t);
+
+ for (i = limit -1; i >= 0; i--)
+ {
+ int parm = actual[i];
+ void *found;
+
+ if (parm == -1) /* skip duplicate */
+ continue;
+
+ found = TreeRemove(t, (void*)&parm);
+ if (found)
+ {
+ printf("%d Tree remove %d %d\n", i, parm, *(int*)(found));
+ free(found);
+ }
+ else
+ {
+ int count = 0;
+ printf("%d %d not found\n", i, parm);
+ traverse(t, parm);
+ for (i = 0; i < limit; i++)
+ if (actual[i] == parm)
+ ++count;
+ printf("%d occurs %d times\n", parm, count);
+ return -2;
+ }
+ if (i % 1000 == 0)
+ {
+ rc = check(t);
+ printf("%d elements, check result %d\n", i+1, rc);
+ if (rc != 0)
+ return 88;
+ }
+ }
+ printf("finished\n");
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc = 0;
+
+ while (rc == 0)
+ rc = test(999999);
+}
+
+#endif
+
+
+
+
+
diff --git a/tts-mqtt-test/src/paho-mqtt/Tree.h b/tts-mqtt-test/src/paho-mqtt/Tree.h
new file mode 100644
index 0000000..bbbd014
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/Tree.h
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial implementation and documentation
+ *******************************************************************************/
+
+
+#if !defined(TREE_H)
+#define TREE_H
+
+#include <stdlib.h> /* for size_t definition */
+
+/*BE
+defm defTree(T) // macro to define a tree
+
+def T concat Node
+{
+ n32 ptr T concat Node "parent"
+ n32 ptr T concat Node "left"
+ n32 ptr T concat Node "right"
+ n32 ptr T id2str(T)
+ n32 suppress "size"
+}
+
+
+def T concat Tree
+{
+ struct
+ {
+ n32 ptr T concat Node suppress "root"
+ n32 ptr DATA suppress "compare"
+ }
+ struct
+ {
+ n32 ptr T concat Node suppress "root"
+ n32 ptr DATA suppress "compare"
+ }
+ n32 dec "count"
+ n32 dec suppress "size"
+}
+
+endm
+
+defTree(INT)
+defTree(STRING)
+defTree(TMP)
+
+BE*/
+
+/**
+ * Structure to hold all data for one list element
+ */
+typedef struct NodeStruct
+{
+ struct NodeStruct *parent, /**< pointer to parent tree node, in case we need it */
+ *child[2]; /**< pointers to child tree nodes 0 = left, 1 = right */
+ void* content; /**< pointer to element content */
+ size_t size; /**< size of content */
+ unsigned int red : 1;
+} Node;
+
+
+/**
+ * Structure to hold all data for one tree
+ */
+typedef struct
+{
+ struct
+ {
+ Node *root; /**< root node pointer */
+ int (*compare)(void*, void*, int); /**< comparison function */
+ } index[2];
+ int indexes, /**< no of indexes into tree */
+ count; /**< no of items */
+ size_t size; /**< heap storage used */
+ unsigned int heap_tracking : 1; /**< switch on heap tracking for this tree? */
+ unsigned int allow_duplicates : 1; /**< switch to allow duplicate entries */
+} Tree;
+
+
+Tree* TreeInitialize(int(*compare)(void*, void*, int));
+void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int));
+void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int));
+
+void* TreeAdd(Tree* aTree, void* content, size_t size);
+
+void* TreeRemove(Tree* aTree, void* content);
+
+void* TreeRemoveKey(Tree* aTree, void* key);
+void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index);
+
+void* TreeRemoveNodeIndex(Tree* aTree, Node* aNode, int index);
+
+void TreeFree(Tree* aTree);
+
+Node* TreeFind(Tree* aTree, void* key);
+Node* TreeFindIndex(Tree* aTree, void* key, int index);
+
+Node* TreeNextElement(Tree* aTree, Node* curnode);
+
+int TreeIntCompare(void* a, void* b, int);
+int TreePtrCompare(void* a, void* b, int);
+int TreeStringCompare(void* a, void* b, int);
+
+#endif
diff --git a/tts-mqtt-test/src/paho-mqtt/VersionInfo.h b/tts-mqtt-test/src/paho-mqtt/VersionInfo.h
new file mode 100644
index 0000000..5b91bf3
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/VersionInfo.h
@@ -0,0 +1,7 @@
+#ifndef VERSIONINFO_H
+#define VERSIONINFO_H
+
+#define BUILD_TIMESTAMP "@BUILD_TIMESTAMP@"
+#define CLIENT_VERSION "@CLIENT_VERSION@"
+
+#endif /* VERSIONINFO_H */
diff --git a/tts-mqtt-test/src/paho-mqtt/mutex_type.h b/tts-mqtt-test/src/paho-mqtt/mutex_type.h
new file mode 100644
index 0000000..5760b37
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/mutex_type.h
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+#if !defined(_MUTEX_TYPE_H_)
+#define _MUTEX_TYPE_H_
+
+#if defined(WIN32) || defined(WIN64)
+ #include <windows.h>
+ #define mutex_type HANDLE
+#else
+ #include <pthread.h>
+ #define mutex_type pthread_mutex_t*
+#endif
+
+#endif /* _MUTEX_TYPE_H_ */
diff --git a/tts-mqtt-test/src/paho-mqtt/utf-8.c b/tts-mqtt-test/src/paho-mqtt/utf-8.c
new file mode 100644
index 0000000..d396477
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/utf-8.c
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+
+/**
+ * @file
+ * \brief Functions for checking that strings contain UTF-8 characters only
+ *
+ * See page 104 of the Unicode Standard 5.0 for the list of well formed
+ * UTF-8 byte sequences.
+ *
+ */
+#include "../paho-mqtt/utf-8.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../paho-mqtt/StackTrace.h"
+
+/**
+ * Macro to determine the number of elements in a single-dimension array
+ */
+#if !defined(ARRAY_SIZE)
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+
+/**
+ * Structure to hold the valid ranges of UTF-8 characters, for each byte up to 4
+ */
+struct
+{
+ int len; /**< number of elements in the following array (1 to 4) */
+ struct
+ {
+ char lower; /**< lower limit of valid range */
+ char upper; /**< upper limit of valid range */
+ } bytes[4]; /**< up to 4 bytes can be used per character */
+}
+valid_ranges[] =
+{
+ {1, { {00, 0x7F} } },
+ {2, { {0xC2, 0xDF}, {0x80, 0xBF} } },
+ {3, { {0xE0, 0xE0}, {0xA0, 0xBF}, {0x80, 0xBF} } },
+ {3, { {0xE1, 0xEC}, {0x80, 0xBF}, {0x80, 0xBF} } },
+ {3, { {0xED, 0xED}, {0x80, 0x9F}, {0x80, 0xBF} } },
+ {3, { {0xEE, 0xEF}, {0x80, 0xBF}, {0x80, 0xBF} } },
+ {4, { {0xF0, 0xF0}, {0x90, 0xBF}, {0x80, 0xBF}, {0x80, 0xBF} } },
+ {4, { {0xF1, 0xF3}, {0x80, 0xBF}, {0x80, 0xBF}, {0x80, 0xBF} } },
+ {4, { {0xF4, 0xF4}, {0x80, 0x8F}, {0x80, 0xBF}, {0x80, 0xBF} } },
+};
+
+
+static const char* UTF8_char_validate(int len, const char* data);
+
+
+/**
+ * Validate a single UTF-8 character
+ * @param len the length of the string in "data"
+ * @param data the bytes to check for a valid UTF-8 char
+ * @return pointer to the start of the next UTF-8 character in "data"
+ */
+static const char* UTF8_char_validate(int len, const char* data)
+{
+ int good = 0;
+ int charlen = 2;
+ int i, j;
+ const char *rc = NULL;
+
+ FUNC_ENTRY;
+ /* first work out how many bytes this char is encoded in */
+ if ((data[0] & 128) == 0)
+ charlen = 1;
+ else if ((data[0] & 0xF0) == 0xF0)
+ charlen = 4;
+ else if ((data[0] & 0xE0) == 0xE0)
+ charlen = 3;
+
+ if (charlen > len)
+ goto exit; /* not enough characters in the string we were given */
+
+ for (i = 0; i < ARRAY_SIZE(valid_ranges); ++i)
+ { /* just has to match one of these rows */
+ if (valid_ranges[i].len == charlen)
+ {
+ good = 1;
+ for (j = 0; j < charlen; ++j)
+ {
+ if (data[j] < valid_ranges[i].bytes[j].lower ||
+ data[j] > valid_ranges[i].bytes[j].upper)
+ {
+ good = 0; /* failed the check */
+ break;
+ }
+ }
+ if (good)
+ break;
+ }
+ }
+
+ if (good)
+ rc = data + charlen;
+ exit:
+ FUNC_EXIT;
+ return rc;
+}
+
+
+/**
+ * Validate a length-delimited string has only UTF-8 characters
+ * @param len the length of the string in "data"
+ * @param data the bytes to check for valid UTF-8 characters
+ * @return 1 (true) if the string has only UTF-8 characters, 0 (false) otherwise
+ */
+int UTF8_validate(int len, const char* data)
+{
+ const char* curdata = NULL;
+ int rc = 0;
+
+ FUNC_ENTRY;
+ if (len == 0)
+ {
+ rc = 1;
+ goto exit;
+ }
+ curdata = UTF8_char_validate(len, data);
+ while (curdata && (curdata < data + len))
+ curdata = UTF8_char_validate(len, curdata);
+
+ rc = curdata != NULL;
+exit:
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+/**
+ * Validate a null-terminated string has only UTF-8 characters
+ * @param string the string to check for valid UTF-8 characters
+ * @return 1 (true) if the string has only UTF-8 characters, 0 (false) otherwise
+ */
+int UTF8_validateString(const char* string)
+{
+ int rc = 0;
+
+ FUNC_ENTRY;
+ rc = UTF8_validate((int)strlen(string), string);
+ FUNC_EXIT_RC(rc);
+ return rc;
+}
+
+
+
+#if defined(UNIT_TESTS)
+#include <stdio.h>
+
+typedef struct
+{
+ int len;
+ char data[20];
+} tests;
+
+tests valid_strings[] =
+{
+ {3, "hjk" },
+ {7, {0x41, 0xE2, 0x89, 0xA2, 0xCE, 0x91, 0x2E} },
+ {3, {'f', 0xC9, 0xB1 } },
+ {9, {0xED, 0x95, 0x9C, 0xEA, 0xB5, 0xAD, 0xEC, 0x96, 0xB4} },
+ {9, {0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE8, 0xAA, 0x9E} },
+ {4, {0x2F, 0x2E, 0x2E, 0x2F} },
+ {7, {0xEF, 0xBB, 0xBF, 0xF0, 0xA3, 0x8E, 0xB4} },
+};
+
+tests invalid_strings[] =
+{
+ {2, {0xC0, 0x80} },
+ {5, {0x2F, 0xC0, 0xAE, 0x2E, 0x2F} },
+ {6, {0xED, 0xA1, 0x8C, 0xED, 0xBE, 0xB4} },
+ {1, {0xF4} },
+};
+
+int main (int argc, char *argv[])
+{
+ int i, failed = 0;
+
+ for (i = 0; i < ARRAY_SIZE(valid_strings); ++i)
+ {
+ if (!UTF8_validate(valid_strings[i].len, valid_strings[i].data))
+ {
+ printf("valid test %d failed\n", i);
+ failed = 1;
+ }
+ else
+ printf("valid test %d passed\n", i);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(invalid_strings); ++i)
+ {
+ if (UTF8_validate(invalid_strings[i].len, invalid_strings[i].data))
+ {
+ printf("invalid test %d failed\n", i);
+ failed = 1;
+ }
+ else
+ printf("invalid test %d passed\n", i);
+ }
+
+ if (failed)
+ printf("Failed\n");
+ else
+ printf("Passed\n");
+
+ return 0;
+} /* End of main function*/
+
+#endif
+
diff --git a/tts-mqtt-test/src/paho-mqtt/utf-8.h b/tts-mqtt-test/src/paho-mqtt/utf-8.h
new file mode 100644
index 0000000..8bce4b3
--- /dev/null
+++ b/tts-mqtt-test/src/paho-mqtt/utf-8.h
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#if !defined(UTF8_H)
+#define UTF8_H
+
+int UTF8_validate(int len, const char *data);
+int UTF8_validateString(const char* string);
+
+#endif
diff --git a/tts-mqtt-test/src/tts-mqtt-test.c b/tts-mqtt-test/src/tts-mqtt-test.c
new file mode 100644
index 0000000..94ac3ae
--- /dev/null
+++ b/tts-mqtt-test/src/tts-mqtt-test.c
@@ -0,0 +1,116 @@
+/* ****************************************************************
+ *
+ * Copyright 2018 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include <tizen.h>
+#include <service_app.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <MQTTClient.h>
+
+#include "log.h"
+
+#define ADDRESS "tcp://MQTTBroker2.us-west-1.elasticbeanstalk.com:1883"
+#define CLIENTID "ExampleClientSub"
+#define QOS 1
+#define TIMEOUT 10000L
+
+volatile MQTTClient_deliveryToken deliveredtoken;
+
+static void _delivered(void *context, MQTTClient_deliveryToken dt)
+{
+ DBG("Message with token value %d delivery confirmed\n", dt);
+ deliveredtoken = dt;
+}
+
+static int __msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
+{
+ char msg_str[1000];
+ strncpy(msg_str, message->payload, message->payloadlen);
+ msg_str[message->payloadlen] = '\0';
+
+ DBG("Message arrived for topic: \"%s\": \"%s\"", topicName, msg_str);
+
+ MQTTClient_freeMessage(&message);
+ MQTTClient_free(topicName);
+
+ return 1;
+}
+
+static void __connlost(void *context, char *cause)
+{
+ DBG("Connection lost\n");
+ DBG(" cause: %s\n", cause);
+}
+
+static bool service_app_create(void *user_data)
+{
+ DBG("service_app_create");
+
+ MQTTClient client;
+ MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
+ int rc;
+
+ char client_id[500];
+ sprintf(client_id, "%s-%d", CLIENTID, rand() % 100000);
+
+ MQTTClient_create(&client, ADDRESS, client_id, MQTTCLIENT_PERSISTENCE_NONE, NULL);
+ conn_opts.keepAliveInterval = 20;
+ conn_opts.cleansession = 1;
+
+ MQTTClient_setCallbacks(client, NULL, __connlost, __msgarrvd, _delivered);
+
+ if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
+ DBG("Failed to connect, return code %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ const char* topic = "/seoul-iot/mqtt/test";
+ DBG("Subscribing to topic \"%s\"", topic);
+ MQTTClient_subscribe(client, topic, QOS);
+
+ return true;
+}
+
+static void service_app_terminate(void *user_data)
+{
+ DBG("service_app_terminate");
+}
+
+static void service_app_control(app_control_h app_control, void *user_data)
+{
+ DBG("service_app_control");
+
+ if (app_control == NULL) {
+ ERR("app_control is NULL");
+ return;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ char ad[50] = {0,};
+ service_app_lifecycle_callback_s event_callback;
+
+ event_callback.create = service_app_create;
+ event_callback.terminate = service_app_terminate;
+ event_callback.app_control = service_app_control;
+
+ return service_app_main(argc, argv, &event_callback, ad);
+}
diff --git a/tts-mqtt-test/tizen-manifest.xml b/tts-mqtt-test/tizen-manifest.xml
new file mode 100644
index 0000000..e64c147
--- /dev/null
+++ b/tts-mqtt-test/tizen-manifest.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="4.0" package="org.tizen.tts-mqtt-test" version="1.0.0">
+ <profile name="iot-headless"/>
+ <service-application appid="org.tizen.tts-mqtt-test" exec="tts-mqtt-test" multiple="false" nodisplay="true" on-boot="true" taskmanage="false" type="capp">
+ <label>tts-mqtt-test</label>
+ <icon>tts-mqtt-test.png</icon>
+ <background-category value="background-network"/>
+ </service-application>
+ <privileges>
+ <privilege>http://tizen.org/privilege/internet</privilege>
+ </privileges>
+</manifest>