From a8c7ca1cae4a67b9496ea8214984f9243ce7983e Mon Sep 17 00:00:00 2001 From: Alvin-lyq <2601685812@qq.com> Date: Sat, 11 Oct 2025 17:34:08 +0800 Subject: [PATCH] remote node --- config.json | 3 +- src/remote/CMakeLists.txt | 64 + src/remote/include/paho_mqtt_3c/Base64.h | 83 + src/remote/include/paho_mqtt_3c/Clients.h | 153 + src/remote/include/paho_mqtt_3c/Heap.h | 82 + src/remote/include/paho_mqtt_3c/LinkedList.h | 105 + src/remote/include/paho_mqtt_3c/Log.h | 85 + src/remote/include/paho_mqtt_3c/MQTTAsync.h | 2068 +++++++ src/remote/include/paho_mqtt_3c/MQTTClient.h | 1670 ++++++ .../paho_mqtt_3c/MQTTClientPersistence.h | 254 + src/remote/include/paho_mqtt_3c/MQTTConnect.h | 148 + src/remote/include/paho_mqtt_3c/MQTTFormat.h | 37 + src/remote/include/paho_mqtt_3c/MQTTPacket.h | 270 + .../include/paho_mqtt_3c/MQTTPacketOut.h | 39 + .../include/paho_mqtt_3c/MQTTPersistence.h | 94 + .../paho_mqtt_3c/MQTTPersistenceDefault.h | 38 + .../include/paho_mqtt_3c/MQTTProperties.h | 225 + .../include/paho_mqtt_3c/MQTTProtocol.h | 46 + .../include/paho_mqtt_3c/MQTTProtocolClient.h | 60 + .../include/paho_mqtt_3c/MQTTProtocolOut.h | 50 + src/remote/include/paho_mqtt_3c/MQTTPublish.h | 38 + .../include/paho_mqtt_3c/MQTTReasonCodes.h | 85 + .../include/paho_mqtt_3c/MQTTSubscribe.h | 39 + .../include/paho_mqtt_3c/MQTTSubscribeOpts.h | 46 + .../include/paho_mqtt_3c/MQTTUnsubscribe.h | 38 + src/remote/include/paho_mqtt_3c/Messages.h | 24 + src/remote/include/paho_mqtt_3c/OsWrapper.h | 42 + src/remote/include/paho_mqtt_3c/SHA1.h | 91 + src/remote/include/paho_mqtt_3c/SSLSocket.h | 52 + src/remote/include/paho_mqtt_3c/Socket.h | 145 + .../include/paho_mqtt_3c/SocketBuffer.h | 84 + src/remote/include/paho_mqtt_3c/StackTrace.h | 71 + src/remote/include/paho_mqtt_3c/Thread.h | 75 + src/remote/include/paho_mqtt_3c/Tree.h | 115 + .../include/paho_mqtt_3c/VersionInfo.h.in | 7 + src/remote/include/paho_mqtt_3c/WebSocket.h | 77 + src/remote/include/paho_mqtt_3c/mutex_type.h | 25 + src/remote/include/paho_mqtt_3c/pubsub_opts.h | 86 + src/remote/include/paho_mqtt_3c/utf-8.h | 23 + src/remote/include/remote/json.h | 2351 ++++++++ src/remote/include/remote/remote_node.hpp | 15 + src/remote/lib/libpaho-mqtt3c-static.a | Bin 0 -> 331658 bytes src/remote/lib/libpaho-mqtt3c.a | Bin 0 -> 362166 bytes src/remote/package.xml | 22 + src/remote/src/jsoncpp.cpp | 5342 +++++++++++++++++ src/remote/src/remote_node.cpp | 282 + 46 files changed, 14748 insertions(+), 1 deletion(-) create mode 100644 src/remote/CMakeLists.txt create mode 100644 src/remote/include/paho_mqtt_3c/Base64.h create mode 100644 src/remote/include/paho_mqtt_3c/Clients.h create mode 100644 src/remote/include/paho_mqtt_3c/Heap.h create mode 100644 src/remote/include/paho_mqtt_3c/LinkedList.h create mode 100644 src/remote/include/paho_mqtt_3c/Log.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTAsync.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTClient.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTClientPersistence.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTConnect.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTFormat.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTPacket.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTPacketOut.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTPersistence.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTPersistenceDefault.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTProperties.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTProtocol.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTProtocolClient.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTProtocolOut.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTPublish.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTReasonCodes.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTSubscribe.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTSubscribeOpts.h create mode 100644 src/remote/include/paho_mqtt_3c/MQTTUnsubscribe.h create mode 100644 src/remote/include/paho_mqtt_3c/Messages.h create mode 100644 src/remote/include/paho_mqtt_3c/OsWrapper.h create mode 100644 src/remote/include/paho_mqtt_3c/SHA1.h create mode 100644 src/remote/include/paho_mqtt_3c/SSLSocket.h create mode 100644 src/remote/include/paho_mqtt_3c/Socket.h create mode 100644 src/remote/include/paho_mqtt_3c/SocketBuffer.h create mode 100644 src/remote/include/paho_mqtt_3c/StackTrace.h create mode 100644 src/remote/include/paho_mqtt_3c/Thread.h create mode 100644 src/remote/include/paho_mqtt_3c/Tree.h create mode 100644 src/remote/include/paho_mqtt_3c/VersionInfo.h.in create mode 100644 src/remote/include/paho_mqtt_3c/WebSocket.h create mode 100644 src/remote/include/paho_mqtt_3c/mutex_type.h create mode 100644 src/remote/include/paho_mqtt_3c/pubsub_opts.h create mode 100644 src/remote/include/paho_mqtt_3c/utf-8.h create mode 100644 src/remote/include/remote/json.h create mode 100644 src/remote/include/remote/remote_node.hpp create mode 100644 src/remote/lib/libpaho-mqtt3c-static.a create mode 100644 src/remote/lib/libpaho-mqtt3c.a create mode 100644 src/remote/package.xml create mode 100644 src/remote/src/jsoncpp.cpp create mode 100644 src/remote/src/remote_node.cpp diff --git a/config.json b/config.json index 43449fd..b199dea 100644 --- a/config.json +++ b/config.json @@ -7,6 +7,7 @@ "external_net_port": 19683, "mqtt_user": "zxwl", "mqtt_password": "zxwl1234@", - "pub_gps_topic": "/zxwl/vehicle/V060002/gps" + "pub_gps_topic": "/zxwl/vehicle/V060002/gps", + "remote_topic": "/zxwl/vehicle/V060002/ctrl" } } \ No newline at end of file diff --git a/src/remote/CMakeLists.txt b/src/remote/CMakeLists.txt new file mode 100644 index 0000000..afec731 --- /dev/null +++ b/src/remote/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 3.5) +project(remote) + +# Default to C99 +if(NOT CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 99) +endif() + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(std_msgs REQUIRED) +find_package(sweeper_interfaces REQUIRED) + +include_directories( + include/remote + include/paho_mqtt_3c +) + +add_executable(remote_node + src/remote_node.cpp + src/jsoncpp.cpp +) + +ament_target_dependencies(remote_node rclcpp std_msgs sweeper_interfaces) + +if(CMAKE_SYSTEM_PROCESSOR MATCHES aarch64) + target_link_libraries( + remote_node + ${PROJECT_SOURCE_DIR}/lib/libpaho-mqtt3c-static.a + ) +else() + target_link_libraries( + remote_node + ${PROJECT_SOURCE_DIR}/lib/libpaho-mqtt3c.a + ) +endif() + +install(TARGETS + remote_node + DESTINATION lib/${PROJECT_NAME} +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # uncomment the line when a copyright and license is not present in all source files + #set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # uncomment the line when this package is not in a git repo + #set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/src/remote/include/paho_mqtt_3c/Base64.h b/src/remote/include/paho_mqtt_3c/Base64.h new file mode 100644 index 0000000..df9dd75 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/Base64.h @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2018 Wind River Systems, Inc. All Rights Reserved. + * + * 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: + * Keith Holman - initial implementation and documentation + *******************************************************************************/ + +#if !defined(BASE64_H) +#define BASE64_H + +/** type for size of a buffer, it saves passing around @p size_t (unsigned long long or unsigned long int) */ +typedef unsigned int b64_size_t; +/** type for raw base64 data */ +typedef unsigned char b64_data_t; + +/** + * Decodes base64 data + * + * @param[out] out decoded data + * @param[in] out_len length of output buffer + * @param[in] in base64 string to decode + * @param[in] in_len length of input buffer + * + * @return the amount of data decoded + * + * @see Base64_decodeLength + * @see Base64_encode + */ +b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, + const char *in, b64_size_t in_len ); + +/** + * Size of buffer required to decode base64 data + * + * @param[in] in base64 string to decode + * @param[in] in_len length of input buffer + * + * @return the size of buffer the decoded string would require + * + * @see Base64_decode + * @see Base64_encodeLength + */ +b64_size_t Base64_decodeLength( const char *in, b64_size_t in_len ); + +/** + * Encodes base64 data + * + * @param[out] out encode base64 string + * @param[in] out_len length of output buffer + * @param[in] in raw data to encode + * @param[in] in_len length of input buffer + * + * @return the amount of data encoded + * + * @see Base64_decode + * @see Base64_encodeLength + */ +b64_size_t Base64_encode( char *out, b64_size_t out_len, + const b64_data_t *in, b64_size_t in_len ); + +/** + * Size of buffer required to encode base64 data + * + * @param[in] in raw data to encode + * @param[in] in_len length of input buffer + * + * @return the size of buffer the encoded string would require + * + * @see Base64_decodeLength + * @see Base64_encode + */ +b64_size_t Base64_encodeLength( const b64_data_t *in, b64_size_t in_len ); + +#endif /* BASE64_H */ diff --git a/src/remote/include/paho_mqtt_3c/Clients.h b/src/remote/include/paho_mqtt_3c/Clients.h new file mode 100644 index 0000000..fd32e3b --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/Clients.h @@ -0,0 +1,153 @@ +/******************************************************************************* + * 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 - add SSL support + * Ian Craggs - fix for bug 413429 - connectionLost not called + * Ian Craggs - change will payload to binary + * Ian Craggs - password to binary + * Ian Craggs - MQTT 5 support + *******************************************************************************/ + +#if !defined(CLIENTS_H) +#define CLIENTS_H + +#include +#if defined(OPENSSL) +#if defined(WIN32) || defined(WIN64) +#include +#endif +#include +#endif +#include "MQTTClient.h" +#include "LinkedList.h" +#include "MQTTClientPersistence.h" + + +/** + * Stored publication data to minimize copying + */ +typedef struct +{ + char *topic; + int topiclen; + char* payload; + int payloadlen; + int refcount; +} Publications; + +/** + * Client publication message data + */ +typedef struct +{ + int qos; + int retain; + int msgid; + int MQTTVersion; + MQTTProperties properties; + Publications *publish; + time_t lastTouch; /**> used for retry and expiry */ + char nextMessageType; /**> PUBREC, PUBREL, PUBCOMP */ + int len; /**> length of the whole structure+data */ +} Messages; + +/** + * Client will message data + */ +typedef struct +{ + char *topic; + int payloadlen; + void *payload; + int retained; + int qos; +} willMessages; + +typedef struct +{ + int socket; + time_t lastSent; + time_t lastReceived; +#if defined(OPENSSL) + SSL* ssl; + SSL_CTX* ctx; +#endif + int websocket; /**< socket has been upgraded to use web sockets */ + char *websocket_key; +} networkHandles; + + +/* connection states */ +/** no connection in progress, see connected value */ +#define NOT_IN_PROGRESS 0x0 +/** TCP connection in progress */ +#define TCP_IN_PROGRESS 0x1 +/** SSL connection in progress */ +#define SSL_IN_PROGRESS 0x2 +/** Websocket connection in progress */ +#define WEBSOCKET_IN_PROGRESS 0x3 +/** TCP completed, waiting for MQTT ACK */ +#define WAIT_FOR_CONNACK 0x4 +/** Disconnecting */ +#define DISCONNECTING -2 + +/** + * 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 V3 clean session flag */ + unsigned int cleanstart : 1; /**< MQTT V5 clean start 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; + signed 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; + int sessionExpiry; /**< MQTT 5 session expiry */ +#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/src/remote/include/paho_mqtt_3c/Heap.h b/src/remote/include/paho_mqtt_3c/Heap.h new file mode 100644 index 0000000..6d24c04 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 +#include + +#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/src/remote/include/paho_mqtt_3c/LinkedList.h b/src/remote/include/paho_mqtt_3c/LinkedList.h new file mode 100644 index 0000000..102a4fd --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 /* 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/src/remote/include/paho_mqtt_3c/Log.h b/src/remote/include/paho_mqtt_3c/Log.h new file mode 100644 index 0000000..455beb6 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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/src/remote/include/paho_mqtt_3c/MQTTAsync.h b/src/remote/include/paho_mqtt_3c/MQTTAsync.h new file mode 100644 index 0000000..7b2067a --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTAsync.h @@ -0,0 +1,2068 @@ +/******************************************************************************* + * 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 + * Ian Craggs - MQTT 5.0 + *******************************************************************************/ + +/********************************************************************/ + +/** + * @cond MQTTAsync_main + * @mainpage Asynchronous MQTT client library for C + * + * © Copyright IBM Corp. 2009, 2018 + * + * @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. + * + * Using the client
+ * Applications that use the client library typically use a similar structure: + *
    + *
  • Create a client object
  • + *
  • Set the options to connect to an MQTT server
  • + *
  • Set up callback functions
  • + *
  • Connect the client to an MQTT server
  • + *
  • Subscribe to any topics the client needs to receive
  • + *
  • Repeat until finished:
  • + *
      + *
    • Publish any messages the client needs to
    • + *
    • Handle any incoming messages
    • + *
    + *
  • Disconnect the client
  • + *
  • Free any memory being used by the client
  • + *
+ * Some simple examples are shown here: + *
    + *
  • @ref publish
  • + *
  • @ref subscribe
  • + *
+ * Additional information about important concepts is provided here: + *
    + *
  • @ref async
  • + *
  • @ref wildcard
  • + *
  • @ref qos
  • + *
  • @ref tracing
  • + *
  • @ref auto_reconnect
  • + *
  • @ref offline_publish
  • + *
+ * @endcond + */ + +/* +/// @cond EXCLUDE +*/ +#if !defined(MQTTASYNC_H) +#define MQTTASYNC_H + +#if defined(__cplusplus) + extern "C" { +#endif + +#if defined(WIN32) || defined(WIN64) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#else + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#endif + +#include +/* +/// @endcond +*/ + +#include "MQTTProperties.h" +#include "MQTTReasonCodes.h" +#include "MQTTSubscribeOpts.h" +#if !defined(NO_PERSISTENCE) +#include "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 + /** + * Return code: don't use options for another version of MQTT + */ + #define MQTTASYNC_BAD_MQTT_OPTION -15 + /** + * Return code: call not applicable to the client's version of MQTT + */ + #define MQTTASYNC_WRONG_MQTT_VERSION -16 + + +/** + * 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 +/** + * MQTT version to connect with: 5 + */ +#define MQTTVERSION_5 5 +/** + * 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 or 1. + * 0 indicates no message properties */ + 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: + *
+ *
QoS0
+ *
Fire and forget - the message may not be delivered
+ *
QoS1
+ *
At least once - the message will be delivered, but may be + * delivered more than once in some circumstances.
+ *
QoS2
+ *
Once and one only - the message will be delivered exactly once.
+ *
+ */ + int qos; + /** + * The retained flag serves two purposes depending on whether the message + * it is associated with is being published or received. + * + * retained = true
+ * 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. + * + * retained = false
+ * For publishers, this indicates 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; + /** + * The MQTT V5 properties associated with the message. + */ + MQTTProperties properties; +} MQTTAsync_message; + +#define MQTTAsync_message_initializer { {'M', 'Q', 'T', 'M'}, 1, 0, NULL, 0, 0, 0, 0, MQTTProperties_initializer } + +/** + * 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 context 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 topicName, otherwise topicLen + * is 0. If topicLen is 0, the value returned by strlen(topicName) + * can be trusted. If topicLen is greater than 0, the full topic name + * can be retrieved by accessing topicName as a byte array of length + * topicLen. + * @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 context 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 context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param cause The reason for the disconnection. + * Currently, cause 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 context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param cause The reason for the disconnection. + * Currently, cause is always set to NULL. + */ +typedef void MQTTAsync_connected(void* context, char* cause); + +/** + * This is a callback function, which will be called when the client + * library receives a disconnect packet. + * @param context A pointer to the context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param properties the properties in the disconnect packet. + * @param properties the reason code from the disconnect packet + * Currently, cause is always set to NULL. + */ +typedef void MQTTAsync_disconnected(void* context, MQTTProperties* properties, + enum MQTTReasonCodes reasonCode); + +/** + * Sets the MQTTAsync_disconnected() 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 context 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_setDisconnected(MQTTAsync handle, void* context, MQTTAsync_disconnected* co); + + +/** 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 an unsuccessful API call in the response callback onFailure. */ +typedef struct +{ + /** The eyecatcher for this structure. Will be MQFD. */ + char struct_id[4]; + /** The version number of this structure. Will be 0 */ + int struct_version; + /** A token identifying the failed request. */ + MQTTAsync_token token; + /** The MQTT reason code returned. */ + enum MQTTReasonCodes reasonCode; + /** The MQTT properties on the ack, if any. */ + MQTTProperties properties; + /** A numeric code identifying the MQTT client library error. */ + int code; + /** Optional further text explaining the error. Can be NULL. */ + const char *message; + /** Packet type on which the failure occurred - used for publish QoS 1/2 exchanges*/ + int packet_type; +} MQTTAsync_failureData5; + +#define MQTTAsync_failureData5_initializer {{'M', 'Q', 'F', 'D'}, 0, 0, MQTTREASONCODE_SUCCESS, MQTTProperties_initializer, 0, NULL} + +/** 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; + + +/** The data returned on completion of a successful API call in the response callback onSuccess. */ +typedef struct +{ + char struct_id[4]; /**< The eyecatcher for this structure. Will be MQSD. */ + int struct_version; /**< The version number of this structure. Will be 0 */ + /** A token identifying the successful request. Can be used to refer to the request later. */ + MQTTAsync_token token; + enum MQTTReasonCodes reasonCode; /**< MQTT V5 reason code returned */ + MQTTProperties properties; /**< MQTT V5 properties returned, if any */ + /** A union of the different values that can be returned for subscribe, unsubscribe and publish. */ + union + { + /** For subscribeMany, the list of reasonCodes returned by the server. */ + struct + { + int reasonCodeCount; /**< the number of reason codes in the reasonCodes array */ + enum MQTTReasonCodes* reasonCodes; /**< an array of reasonCodes */ + } sub; + /** For publish, the message being sent to the server. */ + struct + { + MQTTAsync_message message; /**< the message being sent to the server */ + char* destinationName; /**< the topic destination for the message */ + } pub; + /* For connect, the server connected to, MQTT version used, and sessionPresent flag */ + struct + { + char* serverURI; /**< the connection string of the server */ + int MQTTVersion; /**< the version of MQTT being used */ + int sessionPresent; /**< the session present flag returned from the server */ + } connect; + /** For unsubscribeMany, the list of reasonCodes returned by the server. */ + struct + { + int reasonCodeCount; /**< the number of reason codes in the reasonCodes array */ + enum MQTTReasonCodes* reasonCodes; /**< an array of reasonCodes */ + } unsub; + } alt; +} MQTTAsync_successData5; + +#define MQTTAsync_successData5_initializer {{'M', 'Q', 'S', 'D'}, 0, 0, MQTTREASONCODE_SUCCESS, MQTTProperties_initializer} + +/** + * 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 context 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 MQTT V5 version of ::MQTTAsync_onSuccess. + * 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 context 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_onSuccess5(void* context, MQTTAsync_successData5* 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 context value originally passed to + * ::MQTTAsync_responseOptions, which contains any application-specific context. + * @param response Failure data associated with the API completion. + */ +typedef void MQTTAsync_onFailure(void* context, MQTTAsync_failureData* response); + +/** + * This is a callback function, the MQTT V5 version of ::MQTTAsync_onFailure. + * The 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 context value originally passed to + * ::MQTTAsync_responseOptions, which contains any application-specific context. + * @param response Failure data associated with the API completion. + */ +typedef void MQTTAsync_onFailure5(void* context, MQTTAsync_failureData5* response); + +typedef struct MQTTAsync_responseOptions +{ + /** The eyecatcher for this structure. Must be MQTR */ + char struct_id[4]; + /** The version number of this structure. Must be 0 or 1 + * if 0, no MQTTV5 options */ + 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 context 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; + /** + * 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_onSuccess5* onSuccess5; + /** + * 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_onFailure5* onFailure5; + /** + * MQTT V5 input properties + */ + MQTTProperties properties; + /* + * MQTT V5 subscribe options, when used with subscribe only. + */ + MQTTSubscribe_options subscribeOptions; + /* + * MQTT V5 subscribe option count, when used with subscribeMany only. + * The number of entries in the subscribe_options_list array. + */ + int subscribeOptionsCount; + /* + * MQTT V5 subscribe option array, when used with subscribeMany only. + */ + MQTTSubscribe_options* subscribeOptionsList; +} MQTTAsync_responseOptions; + +#define MQTTAsync_responseOptions_initializer { {'M', 'Q', 'T', 'R'}, 1, NULL, NULL, 0, 0, NULL, NULL, MQTTProperties_initializer, MQTTSubscribe_options_initializer, 0, NULL} + +typedef struct MQTTAsync_responseOptions MQTTAsync_callOptions; +#define MQTTAsync_callOptions_initializer MQTTAsync_responseOptions_initializer + +/** + * 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. + * + * Note: 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 context 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); + +/** + * This function sets the callback function for a connection lost event for + * a specific client. Any necessary message acknowledgements and status + * communications are handled in the background without any intervention + * from the client application. + * + * Note: 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 context pointer is passed 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. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ + +DLLExport int MQTTAsync_setConnectionLostCallback(MQTTAsync handle, void* context, + MQTTAsync_connectionLost* cl); + +/** + * This function sets the callback function for a message arrived event for + * a specific client. 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. + * + * Note: 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 context pointer is passed to the callback functions to provide + * access to the context information in the callback. + * @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. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ +DLLExport int MQTTAsync_setMessageArrivedCallback(MQTTAsync handle, void* context, + MQTTAsync_messageArrived* ma); + +/** + * This function sets the callback function for a delivery complete event + * for a specific client. Any necessary message acknowledgements and status + * communications are handled in the background without any intervention + * from the client application. + * + * Note: 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 context pointer is passed to the callback functions to provide + * access to the context information in the callback. + * @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_setDeliveryCompleteCallback(MQTTAsync handle, void* context, + 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 context 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 protocol://host:port. + * protocol must be tcp or ssl. For host, 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 + * tcp://localhost:1883. + * @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: + *
+ * ::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. + *
+ * ::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. + *
+ * ::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 or 1 + * 0 means no MQTTVersion + */ + 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; + /** Whether the MQTT version is 3.1, 3.1.1, or 5. To use V5, this must be set. + * MQTT V5 has to be chosen here, because during the create call the message persistence + * is initialized, and we want to know whether the format of any persisted messages + * is appropriate for the MQTT version we are going to connect with. Selecting 3.1 or + * 3.1.1 and attempting to read 5.0 persisted messages will result in an error on create. */ + int MQTTVersion; +} MQTTAsync_createOptions; + +#define MQTTAsync_createOptions_initializer { {'M', 'Q', 'C', 'O'}, 0, 0, 100, MQTTVERSION_DEFAULT } + + +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; + + /** + * Callback function for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + int (*ssl_error_cb) (const char *str, size_t len, void *u); + + /** + * Application-specific contex for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + void* ssl_error_context; +} MQTTAsync_SSLOptions; + +#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 3, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, 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 5 or 6. + * 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) + * 5 signifies no MQTTV5 properties + */ + 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 after which unacknowledged publish requests are + * retried during a TCP session. With MQTT 3.1.1 and later, retries are + * not required except on reconnect. 0 turns off in-session retries, and is the + * recommended setting. Adding retries to an already overloaded network only + * exacerbates the problem. + */ + 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 context 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 protocol://host:port. + * protocol must be tcp or ssl. For host, 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 + * tcp://localhost:1883. + */ + 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; + /* + * MQTT V5 clean start flag. Only clears state at the beginning of the session. + */ + int cleanstart; + /** + * MQTT V5 properties for connect + */ + MQTTProperties *connectProperties; + /** + * MQTT V5 properties for the will message in the connect + */ + MQTTProperties *willProperties; + /** + * 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_onSuccess5* onSuccess5; + /** + * 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_onFailure5* onFailure5; +} MQTTAsync_connectOptions; + + +#define MQTTAsync_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 6, 60, 1, 65535, NULL, NULL, NULL, 30, 0,\ +NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_DEFAULT, 0, 1, 60, {0, NULL}, 0, NULL, NULL, NULL, NULL} + +#define MQTTAsync_connectOptions_initializer5 { {'M', 'Q', 'T', 'C'}, 6, 60, 0, 65535, NULL, NULL, NULL, 30, 0,\ +NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_5, 0, 1, 60, {0, NULL}, 1, NULL, NULL, NULL, 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:

+ * 1: Connection refused: Unacceptable protocol version
+ * 2: Connection refused: Identifier rejected
+ * 3: Connection refused: Server unavailable
+ * 4: Connection refused: Bad user name or password
+ * 5: Connection refused: Not authorized
+ * 6-255: Reserved for future use
+ */ +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 V5 properties */ + 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 context pointer is passed to success or failure callback functions to + * provide access to the context information in the callback. + */ + void* context; + /** + * MQTT V5 input properties + */ + MQTTProperties properties; + /** + * Reason code for MQTTV5 disconnect + */ + enum MQTTReasonCodes reasonCode; + /** + * 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_onSuccess5* onSuccess5; + /** + * 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_onFailure5* onFailure5; +} MQTTAsync_disconnectOptions; + +#define MQTTAsync_disconnectOptions_initializer { {'M', 'Q', 'T', 'D'}, 1, 0, NULL, NULL, NULL, MQTTProperties_initializer, MQTTREASONCODE_SUCCESS } + + +/** + * 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 count) of pointers to + * topics, each of which may include wildcards. + * @param qos An array (of length count) 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 count) 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, const 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). + * + * Important note: 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. Important + * note: 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 message 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); + +/** + * Returns a pointer to a string representation of the error code, or NULL. + * Do not free after use. Returns NULL if the error code is unknown. + * @param code the MQTTASYNC_ return code. + * @return a static string representation of the error code. + */ +DLLExport const char* MQTTAsync_strerror(int code); + + +/** + * @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: + *
    + *
  • 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.
  • + *
  • 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.
  • + *
+ * 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: + * + * QoS0, At most once: 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. + * + * QoS1, At least once: 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. + * + * QoS2, Exactly once: 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 +#include +#include +#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 +#include +#include +#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; ipayloadlen; 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 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 + */ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTClient.h b/src/remote/include/paho_mqtt_3c/MQTTClient.h new file mode 100644 index 0000000..518dbbd --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTClient.h @@ -0,0 +1,1670 @@ +/******************************************************************************* + * 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 + * © Copyright IBM Corp. 2009, 2018 + * + * @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 + * MQTT C Client Asynchronous API Documentation. + * 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. + * + * Using the client
+ * Applications that use the client library typically use a similar structure: + *
    + *
  • Create a client object
  • + *
  • Set the options to connect to an MQTT server
  • + *
  • Set up callback functions if multi-threaded (asynchronous mode) + * operation is being used (see @ref async).
  • + *
  • Subscribe to any topics the client needs to receive
  • + *
  • Repeat until finished:
  • + *
      + *
    • Publish any messages the client needs to
    • + *
    • Handle any incoming messages
    • + *
    + *
  • Disconnect the client
  • + *
  • Free any memory being used by the client
  • + *
+ * Some simple examples are shown here: + *
    + *
  • @ref pubsync
  • + *
  • @ref pubasync
  • + *
  • @ref subasync
  • + *
+ * Additional information about important concepts is provided here: + *
    + *
  • @ref async
  • + *
  • @ref wildcard
  • + *
  • @ref qos
  • + *
  • @ref tracing
  • + *
+ * @endcond + */ + +/* +/// @cond EXCLUDE +*/ +#if !defined(MQTTCLIENT_H) +#define MQTTCLIENT_H + +#if defined(__cplusplus) + extern "C" { +#endif + +#if defined(WIN32) || defined(WIN64) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#else + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#endif + +#include +/* +/// @endcond +*/ + +#include "MQTTProperties.h" +#include "MQTTReasonCodes.h" +#include "MQTTSubscribeOpts.h" +#if !defined(NO_PERSISTENCE) +#include "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: unrecognized MQTT version + */ + #define MQTTCLIENT_BAD_MQTT_VERSION -11 +/** + * Return code: protocol prefix in serverURI should be tcp:// or ssl:// + */ +#define MQTTCLIENT_BAD_PROTOCOL -14 + /** + * Return code: option not applicable to the requested version of MQTT + */ + #define MQTTCLIENT_BAD_MQTT_OPTION -15 + /** + * Return code: call not applicable to the requested version of MQTT + */ + #define MQTTCLIENT_WRONG_MQTT_VERSION -16 + + +/** + * 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 + /** + * MQTT version to connect with: 5 + */ + #define MQTTVERSION_5 5 +/** + * 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 or 1 + * 0 indicates no message properties */ + 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: + *
+ *
QoS0
+ *
Fire and forget - the message may not be delivered
+ *
QoS1
+ *
At least once - the message will be delivered, but may be + * delivered more than once in some circumstances.
+ *
QoS2
+ *
Once and one only - the message will be delivered exactly once.
+ *
+ */ + int qos; + /** + * The retained flag serves two purposes depending on whether the message + * it is associated with is being published or received. + * + * retained = true
+ * 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. + * + * retained = false
+ * For publishers, this indicates 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; + /** + * The MQTT V5 properties associated with the message. + */ + MQTTProperties properties; +} MQTTClient_message; + +#define MQTTClient_message_initializer { {'M', 'Q', 'T', 'M'}, 1, 0, NULL, 0, 0, 0, 0, MQTTProperties_initializer } + +/** + * 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 context 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 topicName, otherwise topicLen + * is 0. If topicLen is 0, the value returned by strlen(topicName) + * can be trusted. If topicLen is greater than 0, the full topic name + * can be retrieved by accessing topicName as a byte array of length + * topicLen. + * @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. + * Note:MQTTClient_deliveryComplete() is not called when messages are + * published at QoS0. + * @param context A pointer to the context 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 context value originally passed to + * MQTTClient_setCallbacks(), which contains any application-specific context. + * @param cause The reason for the disconnection. + * Currently, cause 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. + * + * Note: 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 context 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 is a callback function, which will be called when the a disconnect + * packet is received from the server. This applies to MQTT V5 and above only. + * @param context A pointer to the context value originally passed to + * ::MQTTAsync_setDisconnected(), which contains any application-specific context. + * @param properties The MQTT V5 properties received with the disconnect, if any. + * @param reasonCode The MQTT V5 reason code received with the disconnect. + * Currently, cause is always set to NULL. + */ +typedef void MQTTClient_disconnected(void* context, MQTTProperties* properties, + enum MQTTReasonCodes reasonCode); + +/** + * Sets the MQTTClient_disconnected() callback function for a client. This will be called + * if a disconnect packet is received from the server. Only valid for MQTT V5 and above. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param context A pointer to any application-specific context. The + * the context 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 MQTTClient_disconnected() callback + * function. NULL removes the callback setting. + * @return ::MQTTCLIENT_SUCCESS if the callbacks were correctly set, + * ::MQTTCLIENT_FAILURE if an error occurred. + */ +DLLExport int MQTTClient_setDisconnected(MQTTClient handle, void* context, MQTTClient_disconnected* co); + +/** + * This is a callback function, the MQTT V5 version of MQTTClient_deliveryComplete(). + * The client application + * must provide an implementation of this function to enable asynchronous + * notification of the completed delivery of messages. + * 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. + * Note: It is not called when messages are published at QoS0. + * @param context A pointer to the context 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. + * @param packet_type the last received packet type for this completion. For QoS 1 + * always PUBACK. For QoS 2 could be PUBREC or PUBCOMP. + * @param properties the MQTT V5 properties returned with the last packet from the server + * @param reasonCode the reason code returned from the server + */ +typedef void MQTTClient_published(void* context, int dt, int packet_type, MQTTProperties* properties, + enum MQTTReasonCodes reasonCode); + +DLLExport int MQTTClient_setPublished(MQTTClient handle, void* context, MQTTClient_published* co); + +/** + * 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 protocol://host:port. + * Currently, protocol must be tcp or ssl. + * For host, 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 + * tcp://localhost:1883. + * @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: + *
+ * ::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. + *
+ * ::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. + *
+ * ::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); + +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 the MQTT version is 3.1, 3.1.1, or 5. To use V5, this must be set. + * MQTT V5 has to be chosen here, because during the create call the message persistence + * is initialized, and we want to know whether the format of any persisted messages + * is appropriate for the MQTT version we are going to connect with. Selecting 3.1 or + * 3.1.1 and attempting to read 5.0 persisted messages will result in an error on create. */ + int MQTTVersion; +} MQTTClient_createOptions; + +#define MQTTClient_createOptions_initializer { {'M', 'Q', 'C', 'O'}, MQTTVERSION_DEFAULT } + +/** + * A version of :MQTTClient_create() with additional options. + * 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 protocol://host:port. + * Currently, protocol must be tcp or ssl. + * For host, 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 + * tcp://localhost:1883. + * @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: + *
+ * ::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. + *
+ * ::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. + *
+ * ::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. + * @param options additional options for the create. + * @return ::MQTTCLIENT_SUCCESS if the client is successfully created, otherwise + * an error code is returned. + */ +DLLExport int MQTTClient_createWithOptions(MQTTClient* handle, const char* serverURI, const char* clientId, + int persistence_type, void* persistence_context, MQTTClient_createOptions* options); + +/** + * 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; + + /** + * Callback function for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + int (*ssl_error_cb) (const char *str, size_t len, void *u); + + /** + * Application-specific contex for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + void* ssl_error_context; + +} MQTTClient_SSLOptions; + +#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 3, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL } + +/** + * MQTTClient_connectOptions defines several settings that control the way the + * client connects to an MQTT server. + * + * Note: 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 must 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, 5 or 6. + * 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 + * 5 signifies no maxInflightMessages and cleanstart + */ + 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 reliable 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 after which unacknowledged publish requests are + * retried during a TCP session. With MQTT 3.1.1 and later, retries are + * not required except on reconnect. 0 turns off in-session retries, and is the + * recommended setting. Adding retries to an already overloaded network only + * exacerbates the problem. + */ + 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 protocol://host:port. + * protocol must be tcp or ssl. For host, 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 + * tcp://localhost:1883. + * 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 + * MQTTVERSION_5 (5) = only try version 5.0 + */ + 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; + /** + * The maximum number of messages in flight + */ + int maxInflightMessages; + /* + * MQTT V5 clean start flag. Only clears state at the beginning of the session. + */ + int cleanstart; +} MQTTClient_connectOptions; + +#define MQTTClient_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 6, 60, 1, 1, NULL, NULL, NULL, 30, 0, NULL, 0, NULL, MQTTVERSION_DEFAULT, {NULL, 0, 0}, {0, NULL}, -1, 0} + +#define MQTTClient_connectOptions_initializer5 { {'M', 'Q', 'T', 'C'}, 6, 60, 0, 1, NULL, NULL, NULL, 30, 0, NULL, 0, NULL, MQTTVERSION_5, {NULL, 0, 0}, {0, NULL}, -1, 1} + +/** + * 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:

+ * 1: Connection refused: Unacceptable protocol version
+ * 2: Connection refused: Identifier rejected
+ * 3: Connection refused: Server unavailable
+ * 4: Connection refused: Bad user name or password
+ * 5: Connection refused: Not authorized
+ * 6-255: Reserved for future use
+ */ +DLLExport int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options); + + +typedef struct MQTTResponse +{ + int version; + enum MQTTReasonCodes reasonCode; + int reasonCodeCount; /* used for subscribeMany5 and unsubscribeMany5 */ + enum MQTTReasonCodes* reasonCodes; /* used for subscribeMany5 and unsubscribeMany5 */ + MQTTProperties* properties; /* optional */ +} MQTTResponse; + +#define MQTTResponse_initializer {1, MQTTREASONCODE_SUCCESS, 0, NULL, NULL} + +DLLExport void MQTTResponse_free(MQTTResponse response); + +DLLExport MQTTResponse MQTTClient_connect5(MQTTClient handle, MQTTClient_connectOptions* options, + MQTTProperties* connectProperties, MQTTProperties* willProperties); + +/** + * 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); + +DLLExport int MQTTClient_disconnect5(MQTTClient handle, int timeout, enum MQTTReasonCodes reason, MQTTProperties* props); + +/** + * 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); + + +DLLExport MQTTResponse MQTTClient_subscribe5(MQTTClient handle, const char* topic, int qos, + MQTTSubscribe_options* opts, MQTTProperties* props); + +/** + * 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 count) of pointers to + * topics, each of which may include wildcards. + * @param qos An array (of length count) 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); + +DLLExport MQTTResponse MQTTClient_subscribeMany5(MQTTClient handle, int count, char* const* topic, + int* qos, MQTTSubscribe_options* opts, MQTTProperties* props); + +/** + * 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); + +DLLExport MQTTResponse MQTTClient_unsubscribe5(MQTTClient handle, const char* topic, MQTTProperties* props); + +/** + * 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 count) 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); + +DLLExport MQTTResponse MQTTClient_unsubscribeMany5(MQTTClient handle, int count, char* const* topic, MQTTProperties* props); + +/** + * 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, const void* payload, int qos, int retained, + MQTTClient_deliveryToken* dt); + +DLLExport MQTTResponse MQTTClient_publish5(MQTTClient handle, const char* topicName, int payloadlen, const void* payload, + int qos, int retained, MQTTProperties* properties, 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); + + +DLLExport MQTTResponse MQTTClient_publishMessage5(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). + * + * Important note: 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()). + * + * Important note: 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 topicName 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 + * topicLen. + * @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 message 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 message 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. Important + * note: 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); + + +enum MQTTCLIENT_TRACE_LEVELS +{ + MQTTCLIENT_TRACE_MAXIMUM = 1, + MQTTCLIENT_TRACE_MEDIUM, + MQTTCLIENT_TRACE_MINIMUM, + MQTTCLIENT_TRACE_PROTOCOL, + MQTTCLIENT_TRACE_ERROR, + MQTTCLIENT_TRACE_SEVERE, + MQTTCLIENT_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 MQTTClient_setTraceLevel(enum MQTTCLIENT_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 message 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 MQTTClient_traceCallback(enum MQTTCLIENT_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 MQTTClient_setTraceCallback(MQTTClient_traceCallback* callback); + +/** + * Returns a pointer to the string representation of the error or NULL. + * + * Do not free after use. Returns NULL if the error code is unknown. + */ +DLLExport const char* MQTTClient_strerror(int code); + +#ifdef __cplusplus + } +#endif + +#endif + +/** + * @cond MQTTClient_main + * @page async Asynchronous vs synchronous client applications + * The client library supports two modes of operation. These are referred to + * as synchronous and asynchronous 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: + *
    + *
  • 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.
  • + *
  • 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.
  • + *
+ * 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: + * + * QoS0, At most once: 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. + * + * QoS1, At least once: 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. + * + * QoS2, Exactly once: 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 +#include +#include +#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 +#include +#include +#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; ipayloadlen; 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 +#include +#include +#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; ipayloadlen; 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 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/src/remote/include/paho_mqtt_3c/MQTTClientPersistence.h b/src/remote/include/paho_mqtt_3c/MQTTClientPersistence.h new file mode 100644 index 0000000..4c9014d --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 persistence_context 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 persistence_type 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 persistence_type. To implement a + * custom persistence mechanism, the application must pass an initialized + * ::MQTTClient_persistence structure as the persistence_context + * 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 persistence_type value specifies the default file system-based + * persistence mechanism (see MQTTClient_create()). + */ +#define MQTTCLIENT_PERSISTENCE_DEFAULT 0 +/** + * This persistence_type value specifies a memory-based + * persistence mechanism (see MQTTClient_create()). + */ +#define MQTTCLIENT_PERSISTENCE_NONE 1 +/** + * This persistence_type 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 clientid in conjunction with the + * serverURI 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 key. + * @param buflens An array of lengths of the data buffers. buflen[n] + * gives the length of buffer[n]. + * @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 + * buffer 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. keys + * 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/src/remote/include/paho_mqtt_3c/MQTTConnect.h b/src/remote/include/paho_mqtt_3c/MQTTConnect.h new file mode 100644 index 0000000..98c2c16 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTConnect.h @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2014, 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 connack return code definitions + * Xiang Rong - 442039 Add makefile to Embedded C client + * Ian Craggs - fix for issue #64, bit order in connack response + *******************************************************************************/ + +#ifndef MQTTCONNECT_H_ +#define MQTTCONNECT_H_ + +enum connack_return_codes +{ + MQTT_CONNECTION_ACCEPTED = 0, + MQTT_UNNACCEPTABLE_PROTOCOL = 1, + MQTT_CLIENTID_REJECTED = 2, + MQTT_SERVER_UNAVAILABLE = 3, + MQTT_BAD_USERNAME_OR_PASSWORD = 4, + MQTT_NOT_AUTHORIZED = 5, +}; + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + + +typedef union +{ + unsigned char all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + unsigned int username : 1; /**< 3.1 user name */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int will : 1; /**< will flag */ + unsigned int cleansession : 1; /**< clean session flag */ + unsigned int : 1; /**< unused */ + } bits; +#else + struct + { + unsigned int : 1; /**< unused */ + unsigned int cleansession : 1; /**< cleansession flag */ + unsigned int will : 1; /**< will flag */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int username : 1; /**< 3.1 user name */ + } bits; +#endif +} MQTTConnectFlags; /**< connect flags byte */ + + + +/** + * Defines the MQTT "Last Will and Testament" (LWT) settings for + * the connect packet. + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTW. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** The LWT topic to which the LWT message will be published. */ + MQTTString topicName; + /** The LWT payload. */ + MQTTString message; + /** + * The retained flag for the LWT message (see MQTTAsync_message.retained). + */ + unsigned char retained; + /** + * The quality of service setting for the LWT message (see + * MQTTAsync_message.qos and @ref qos). + */ + char qos; +} MQTTPacket_willOptions; + + +#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } + + +typedef struct +{ + /** The eyecatcher for this structure. must be MQTC. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 + */ + unsigned char MQTTVersion; + MQTTString clientID; + unsigned short keepAliveInterval; + unsigned char cleansession; + unsigned char willFlag; + MQTTPacket_willOptions will; + MQTTString username; + MQTTString password; +} MQTTPacket_connectData; + +typedef union +{ + unsigned char all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int reserved : 7; /**< unused */ + unsigned int sessionpresent : 1; /**< session present flag */ + } bits; +#else + struct + { + unsigned int sessionpresent : 1; /**< session present flag */ + unsigned int reserved: 7; /**< unused */ + } bits; +#endif +} MQTTConnackFlags; /**< connack flags byte */ + +#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ + MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } + +DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); +DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); +DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); + +DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); +DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); + +#endif /* MQTTCONNECT_H_ */ diff --git a/src/remote/include/paho_mqtt_3c/MQTTFormat.h b/src/remote/include/paho_mqtt_3c/MQTTFormat.h new file mode 100644 index 0000000..47b0c41 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTFormat.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#if !defined(MQTTFORMAT_H) +#define MQTTFORMAT_H + +#include "StackTrace.h" +#include "MQTTPacket.h" + +const char* MQTTPacket_getName(unsigned short packetid); +int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data); +int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent); +int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, + unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen); +int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid); +int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int requestedQoSs[]); +int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs); +int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]); +char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); +char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTPacket.h b/src/remote/include/paho_mqtt_3c/MQTTPacket.h new file mode 100644 index 0000000..350886e --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTPacket.h @@ -0,0 +1,270 @@ +/******************************************************************************* + * 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 - MQTT 3.1.1 support + * Ian Craggs - big endian Linux reversed definition + * Ian Craggs - MQTT 5.0 support + *******************************************************************************/ + +#if !defined(MQTTPACKET_H) +#define MQTTPACKET_H + +#include "Socket.h" +#if defined(OPENSSL) +#include "SSLSocket.h" +#endif +#include "LinkedList.h" +#include "Clients.h" + +typedef unsigned int bool; +typedef void* (*pf)(int, unsigned char, char*, size_t); + +#include "MQTTProperties.h" +#include "MQTTReasonCodes.h" + +enum errors +{ + MQTTPACKET_BAD = -4, + MQTTPACKET_BUFFER_TOO_SHORT = -2, + MQTTPACKET_READ_ERROR = -1, + MQTTPACKET_READ_COMPLETE +}; + + +enum msgTypes +{ + CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, + PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, + PINGREQ, PINGRESP, DISCONNECT, AUTH +}; + +#if defined(__linux__) +#include +#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 */ + unsigned char rc; /**< connack reason code */ + unsigned int MQTTVersion; /**< the version of MQTT */ + MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */ +} Connack; + + +/** + * Data for a packet with header only. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ +} MQTTPacket; + + +/** + * Data for a suback packet. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + int msgId; /**< MQTT message id */ + int MQTTVersion; /**< the version of MQTT */ + MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */ + List* qoss; /**< list of granted QoSs (MQTT 3/4) / reason codes (MQTT 5) */ +} Suback; + + +/** + * Data for an MQTT V5 unsuback packet. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + int msgId; /**< MQTT message id */ + int MQTTVersion; /**< the version of MQTT */ + MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */ + List* reasonCodes; /**< list of reason codes */ +} Unsuback; + + +/** + * 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 */ + int MQTTVersion; /**< the version of MQTT */ + MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */ +} Publish; + + +/** + * Data for one of the ack packets. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + int msgId; /**< MQTT message id */ + unsigned char rc; /**< MQTT 5 reason code */ + int MQTTVersion; /**< the version of MQTT */ + MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */ +} Ack; + +typedef Ack Puback; +typedef Ack Pubrec; +typedef Ack Pubrel; +typedef Ack Pubcomp; + +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(int MQTTVersion, networkHandles* net, int* error); +int MQTTPacket_send(networkHandles* net, Header header, char* buffer, size_t buflen, int free, int MQTTVersion); +int MQTTPacket_sends(networkHandles* net, Header header, int count, char** buffers, size_t* buflens, int* frees, int MQTTVersion); + +void* MQTTPacket_header_only(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen); +int MQTTPacket_send_disconnect(Clients* client, enum MQTTReasonCodes reason, MQTTProperties* props); + +void* MQTTPacket_publish(int MQTTVersion, 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(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen); + +void MQTTPacket_freeAck(Ack* pack); +void MQTTPacket_freeSuback(Suback* pack); +void MQTTPacket_freeUnsuback(Unsuback* 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); + +void MQTTPacket_free_packet(MQTTPacket* pack); + +void writeInt4(char** pptr, int anInt); +int readInt4(char** pptr); +void writeMQTTLenString(char** pptr, MQTTLenString lenstring); +int MQTTLenStringRead(MQTTLenString* lenstring, char** pptr, char* enddata); +int MQTTPacket_VBIlen(int rem_len); +int MQTTPacket_decodeBuf(char* buf, int* value); + +#include "MQTTPacketOut.h" + +#endif /* MQTTPACKET_H */ diff --git a/src/remote/include/paho_mqtt_3c/MQTTPacketOut.h b/src/remote/include/paho_mqtt_3c/MQTTPacketOut.h new file mode 100644 index 0000000..4e0ae10 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTPacketOut.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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 - MQTT 3.1.1 support + * Ian Craggs - MQTT 5.0 support + *******************************************************************************/ + +#if !defined(MQTTPACKETOUT_H) +#define MQTTPACKETOUT_H + +#include "MQTTPacket.h" + +int MQTTPacket_send_connect(Clients* client, int MQTTVersion, + MQTTProperties* connectProperties, MQTTProperties* willProperties); +void* MQTTPacket_connack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen); +void MQTTPacket_freeConnack(Connack* pack); + +int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID); + +int MQTTPacket_send_subscribe(List* topics, List* qoss, MQTTSubscribe_options* opts, MQTTProperties* props, + int msgid, int dup, Clients* client); +void* MQTTPacket_suback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen); + +int MQTTPacket_send_unsubscribe(List* topics, MQTTProperties* props, int msgid, int dup, Clients* client); +void* MQTTPacket_unsuback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen); + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTPersistence.h b/src/remote/include/paho_mqtt_3c/MQTTPersistence.h new file mode 100644 index 0000000..93edb2b --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTPersistence.h @@ -0,0 +1,94 @@ +/******************************************************************************* + * 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 432903 - queue persistence + * Ian Craggs - MQTT V5 updates + *******************************************************************************/ + +#if !defined(MQTTPERSISTENCE_H) +#define MQTTPERSISTENCE_H + +#if defined(__cplusplus) + extern "C" { +#endif + +#include "Clients.h" +#include "MQTTProperties.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 a sent MQTT V5 PUBLISH QoS1 or QoS2 */ +#define PERSISTENCE_V5_PUBLISH_SENT "s5-" +/** Stem of the key for a sent MQTT V5 PUBREL */ +#define PERSISTENCE_V5_PUBREL "sc5-" +/** Stem of the key for a received MQTT V5 PUBLISH QoS2 */ +#define PERSISTENCE_V5_PUBLISH_RECEIVED "r5-" + +/** Stem of the key for an async client command */ +#define PERSISTENCE_COMMAND_KEY "c-" +/** Stem of the key for an MQTT V5 async client command */ +#define PERSISTENCE_V5_COMMAND_KEY "c-" +/** Stem of the key for an async client message queue */ +#define PERSISTENCE_QUEUE_KEY "q-" +/** Stem of the key for an MQTT V5 message queue */ +#define PERSISTENCE_V5_QUEUE_KEY "q5-" +#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(int MQTTVersion, 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 MQTTVersion); +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; + MQTTProperties properties; +} 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 + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTPersistenceDefault.h b/src/remote/include/paho_mqtt_3c/MQTTPersistenceDefault.h new file mode 100644 index 0000000..9c1034c --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTPersistenceDefault.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ + +#if !defined(MQTTPERSISTENCEDEFAULT_H) +#define MQTTPERSISTENCEDEFAULT_H + +/** 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); + +#endif + diff --git a/src/remote/include/paho_mqtt_3c/MQTTProperties.h b/src/remote/include/paho_mqtt_3c/MQTTProperties.h new file mode 100644 index 0000000..deec124 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTProperties.h @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2017, 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 + *******************************************************************************/ + +#if !defined(MQTTPROPERTIES_H) +#define MQTTPROPERTIES_H + +#define MQTT_INVALID_PROPERTY_ID -2 + +/** The one byte MQTT V5 property indicator */ +enum MQTTPropertyCodes { + MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR = 1, /**< The value is 1 */ + MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL = 2, /**< The value is 2 */ + MQTTPROPERTY_CODE_CONTENT_TYPE = 3, /**< The value is 3 */ + MQTTPROPERTY_CODE_RESPONSE_TOPIC = 8, /**< The value is 8 */ + MQTTPROPERTY_CODE_CORRELATION_DATA = 9, /**< The value is 9 */ + MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER = 11, /**< The value is 11 */ + MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL = 17, /**< The value is 17 */ + MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER = 18,/**< The value is 18 */ + MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE = 19, /**< The value is 19 */ + MQTTPROPERTY_CODE_AUTHENTICATION_METHOD = 21, /**< The value is 21 */ + MQTTPROPERTY_CODE_AUTHENTICATION_DATA = 22, /**< The value is 22 */ + MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION = 23,/**< The value is 23 */ + MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL = 24, /**< The value is 24 */ + MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION = 25,/**< The value is 25 */ + MQTTPROPERTY_CODE_RESPONSE_INFORMATION = 26, /**< The value is 26 */ + MQTTPROPERTY_CODE_SERVER_REFERENCE = 28, /**< The value is 28 */ + MQTTPROPERTY_CODE_REASON_STRING = 31, /**< The value is 31 */ + MQTTPROPERTY_CODE_RECEIVE_MAXIMUM = 33, /**< The value is 33*/ + MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM = 34, /**< The value is 34 */ + MQTTPROPERTY_CODE_TOPIC_ALIAS = 35, /**< The value is 35 */ + MQTTPROPERTY_CODE_MAXIMUM_QOS = 36, /**< The value is 36 */ + MQTTPROPERTY_CODE_RETAIN_AVAILABLE = 37, /**< The value is 37 */ + MQTTPROPERTY_CODE_USER_PROPERTY = 38, /**< The value is 38 */ + MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE = 39, /**< The value is 39 */ + MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE = 40,/**< The value is 40 */ + MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE = 41,/**< The value is 41 */ + MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE = 42/**< The value is 241 */ +}; + +#if defined(WIN32) || defined(WIN64) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#else + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#endif + +/** + * Returns a printable string description of an MQTT V5 property code. + * @param value an MQTT V5 property code. + * @return the printable string description of the input property code. + * NULL if the code was not found. + */ +DLLExport const char* MQTTPropertyName(enum MQTTPropertyCodes value); + +/** The one byte MQTT V5 property type */ +enum MQTTPropertyTypes { + MQTTPROPERTY_TYPE_BYTE, + MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER, + MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER, + MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER, + MQTTPROPERTY_TYPE_BINARY_DATA, + MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING, + MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR +}; + +/** + * Returns the MQTT V5 type code of an MQTT V5 property. + * @param value an MQTT V5 property code. + * @return the MQTT V5 type code of the input property. -1 if the code was not found. + */ +DLLExport int MQTTProperty_getType(enum MQTTPropertyCodes value); + +/** + * The data for a length delimited string + */ +typedef struct +{ + int len; /**< the length of the string */ + char* data; /**< pointer to the string data */ +} MQTTLenString; + + +/** + * Structure to hold an MQTT version 5 property of any type + */ +typedef struct +{ + enum MQTTPropertyCodes identifier; /**< The MQTT V5 property id. A multi-byte integer. */ + /** The value of the property, as a union of the different possible types. */ + union { + char byte; /**< holds the value of a byte property type */ + short integer2; /**< holds the value of a 2 byte integer property type */ + int integer4; /**< holds the value of a 4 byte integer property type */ + struct { + MQTTLenString data; /**< The value of a string property, or the name of a user property. */ + MQTTLenString value; /**< The value of a user property. */ + }; + } value; +} MQTTProperty; + +/** + * MQTT version 5 property list + */ +typedef struct MQTTProperties +{ + int count; /**< number of property entries in the array */ + int max_count; /**< max number of properties that the currently allocated array can store */ + int length; /**< mbi: byte length of all properties */ + MQTTProperty *array; /**< array of properties */ +} MQTTProperties; + +#define MQTTProperties_initializer {0, 0, 0, NULL} + +/** + * Returns the length of the properties structure when serialized ready for network transmission. + * @param props an MQTT V5 property structure. + * @return the length in bytes of the properties when serialized. + */ +int MQTTProperties_len(MQTTProperties* props); + +/** + * Add a property pointer to the property array. There is no memory allocation. + * @param props The property list to add the property to. + * @param prop The property to add to the list. + * @return 0 on success, -1 on failure. + */ +DLLExport int MQTTProperties_add(MQTTProperties* props, const MQTTProperty* prop); + +/** + * Serialize the given property list to a character buffer, e.g. for writing to the network. + * @param pptr pointer to the buffer - move the pointer as we add data + * @param properties pointer to the property list, can be NULL + * @return whether the write succeeded or not: number of bytes written, or < 0 on failure. + */ +int MQTTProperties_write(char** pptr, const MQTTProperties* properties); + +/** + * Reads a property list from a character buffer into an array. + * @param properties pointer to the property list to be filled. Should be initalized but empty. + * @param pptr pointer to the character buffer. + * @param enddata pointer to the end of the character buffer so we don't read beyond. + * @return 1 if the properties were read successfully. + */ +int MQTTProperties_read(MQTTProperties* properties, char** pptr, char* enddata); + +/** + * Free all memory allocated to the property list, including any to individual properties. + * @param properties pointer to the property list. + */ +DLLExport void MQTTProperties_free(MQTTProperties* properties); + +/** + * Copy the contents of a property list, allocating additional memory if needed. + * @param props pointer to the property list. + * @return the duplicated property list. + */ +DLLExport MQTTProperties MQTTProperties_copy(const MQTTProperties* props); + +/** + * Checks if property list contains a specific property. + * @param props pointer to the property list. + * @param propid the property id to check for. + * @return 1 if found, 0 if not. + */ +DLLExport int MQTTProperties_hasProperty(MQTTProperties *props, enum MQTTPropertyCodes propid); + +/** + * Returns the number of instances of a property id. Most properties can exist only once. + * User properties and subscription ids can exist more than once. + * @param props pointer to the property list. + * @param propid the property id to check for. + * @return the number of times found. Can be 0. + */ +DLLExport int MQTTProperties_propertyCount(MQTTProperties *props, enum MQTTPropertyCodes propid); + +/** + * Returns the integer value of a specific property. The property given must be a numeric type. + * @param props pointer to the property list. + * @param propid the property id to check for. + * @return the integer value of the property. -9999999 on failure. + */ +DLLExport int MQTTProperties_getNumericValue(MQTTProperties *props, enum MQTTPropertyCodes propid); + +/** + * Returns the integer value of a specific property when it's not the only instance. + * The property given must be a numeric type. + * @param props pointer to the property list. + * @param propid the property id to check for. + * @param index the instance number, starting at 0. + * @return the integer value of the property. -9999999 on failure. + */ +DLLExport int MQTTProperties_getNumericValueAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index); + +/** + * Returns a pointer to the property structure for a specific property. + * @param props pointer to the property list. + * @param propid the property id to check for. + * @return the pointer to the property structure if found. NULL if not found. + */ +DLLExport MQTTProperty* MQTTProperties_getProperty(MQTTProperties *props, enum MQTTPropertyCodes propid); + +/** + * Returns a pointer to the property structure for a specific property when it's not the only instance. + * @param props pointer to the property list. + * @param propid the property id to check for. + * @param index the instance number, starting at 0. + * @return the pointer to the property structure if found. NULL if not found. + */ +DLLExport MQTTProperty* MQTTProperties_getPropertyAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index); + +#endif /* MQTTPROPERTIES_H */ diff --git a/src/remote/include/paho_mqtt_3c/MQTTProtocol.h b/src/remote/include/paho_mqtt_3c/MQTTProtocol.h new file mode 100644 index 0000000..7478103 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 "LinkedList.h" +#include "MQTTPacket.h" +#include "Clients.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 "MQTTProtocolOut.h" + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTProtocolClient.h b/src/remote/include/paho_mqtt_3c/MQTTProtocolClient.h new file mode 100644 index 0000000..92b43a8 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTProtocolClient.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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 "LinkedList.h" +#include "MQTTPacket.h" +#include "Log.h" +#include "MQTTProtocol.h" +#include "Messages.h" +#include "MQTTProperties.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/src/remote/include/paho_mqtt_3c/MQTTProtocolOut.h b/src/remote/include/paho_mqtt_3c/MQTTProtocolOut.h new file mode 100644 index 0000000..e2c2645 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTProtocolOut.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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 - MQTT 3.1.1 support + * Ian Craggs - SNI support + * Ian Craggs - MQTT 5.0 support + *******************************************************************************/ + +#if !defined(MQTTPROTOCOLOUT_H) +#define MQTTPROTOCOLOUT_H + +#include "LinkedList.h" +#include "MQTTPacket.h" +#include "Clients.h" +#include "Log.h" +#include "Messages.h" +#include "MQTTProtocol.h" +#include "MQTTProtocolClient.h" + +#define DEFAULT_PORT 1883 + +size_t MQTTProtocol_addressPort(const char* uri, int* port, const char **topic); +void MQTTProtocol_reconnect(const char* ip_address, Clients* client); +#if defined(OPENSSL) +int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int ssl, int websocket, int MQTTVersion, + MQTTProperties* connectProperties, MQTTProperties* willProperties); +#else +int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int websocket, int MQTTVersion, + MQTTProperties* connectProperties, MQTTProperties* willProperties); +#endif +int MQTTProtocol_handlePingresps(void* pack, int sock); +int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss, int msgID, + MQTTSubscribe_options* opts, MQTTProperties* props); +int MQTTProtocol_handleSubacks(void* pack, int sock); +int MQTTProtocol_unsubscribe(Clients* client, List* topics, int msgID, MQTTProperties* props); +int MQTTProtocol_handleUnsubacks(void* pack, int sock); + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTPublish.h b/src/remote/include/paho_mqtt_3c/MQTTPublish.h new file mode 100644 index 0000000..ebe479d --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTPublish.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 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 + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTPUBLISH_H_ +#define MQTTPUBLISH_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, + MQTTString topicName, unsigned char* payload, int payloadlen); + +DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, + unsigned char** payload, int* payloadlen, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); +DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); +DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); + +#endif /* MQTTPUBLISH_H_ */ diff --git a/src/remote/include/paho_mqtt_3c/MQTTReasonCodes.h b/src/remote/include/paho_mqtt_3c/MQTTReasonCodes.h new file mode 100644 index 0000000..369543b --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTReasonCodes.h @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2017, 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 + *******************************************************************************/ + +#if !defined(MQTTREASONCODES_H) +#define MQTTREASONCODES_H + +/** The MQTT V5 one byte reason code */ +enum MQTTReasonCodes { + MQTTREASONCODE_SUCCESS = 0, + MQTTREASONCODE_NORMAL_DISCONNECTION = 0, + MQTTREASONCODE_GRANTED_QOS_0 = 0, + MQTTREASONCODE_GRANTED_QOS_1 = 1, + MQTTREASONCODE_GRANTED_QOS_2 = 2, + MQTTREASONCODE_DISCONNECT_WITH_WILL_MESSAGE = 4, + MQTTREASONCODE_NO_MATCHING_SUBSCRIBERS = 16, + MQTTREASONCODE_NO_SUBSCRIPTION_FOUND = 17, + MQTTREASONCODE_CONTINUE_AUTHENTICATION = 24, + MQTTREASONCODE_RE_AUTHENTICATE = 25, + MQTTREASONCODE_UNSPECIFIED_ERROR = 128, + MQTTREASONCODE_MALFORMED_PACKET = 129, + MQTTREASONCODE_PROTOCOL_ERROR = 130, + MQTTREASONCODE_IMPLEMENTATION_SPECIFIC_ERROR = 131, + MQTTREASONCODE_UNSUPPORTED_PROTOCOL_VERSION = 132, + MQTTREASONCODE_CLIENT_IDENTIFIER_NOT_VALID = 133, + MQTTREASONCODE_BAD_USER_NAME_OR_PASSWORD = 134, + MQTTREASONCODE_NOT_AUTHORIZED = 135, + MQTTREASONCODE_SERVER_UNAVAILABLE = 136, + MQTTREASONCODE_SERVER_BUSY = 137, + MQTTREASONCODE_BANNED = 138, + MQTTREASONCODE_SERVER_SHUTTING_DOWN = 139, + MQTTREASONCODE_BAD_AUTHENTICATION_METHOD = 140, + MQTTREASONCODE_KEEP_ALIVE_TIMEOUT = 141, + MQTTREASONCODE_SESSION_TAKEN_OVER = 142, + MQTTREASONCODE_TOPIC_FILTER_INVALID = 143, + MQTTREASONCODE_TOPIC_NAME_INVALID = 144, + MQTTREASONCODE_PACKET_IDENTIFIER_IN_USE = 145, + MQTTREASONCODE_PACKET_IDENTIFIER_NOT_FOUND = 146, + MQTTREASONCODE_RECEIVE_MAXIMUM_EXCEEDED = 147, + MQTTREASONCODE_TOPIC_ALIAS_INVALID = 148, + MQTTREASONCODE_PACKET_TOO_LARGE = 149, + MQTTREASONCODE_MESSAGE_RATE_TOO_HIGH = 150, + MQTTREASONCODE_QUOTA_EXCEEDED = 151, + MQTTREASONCODE_ADMINISTRATIVE_ACTION = 152, + MQTTREASONCODE_PAYLOAD_FORMAT_INVALID = 153, + MQTTREASONCODE_RETAIN_NOT_SUPPORTED = 154, + MQTTREASONCODE_QOS_NOT_SUPPORTED = 155, + MQTTREASONCODE_USE_ANOTHER_SERVER = 156, + MQTTREASONCODE_SERVER_MOVED = 157, + MQTTREASONCODE_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 158, + MQTTREASONCODE_CONNECTION_RATE_EXCEEDED = 159, + MQTTREASONCODE_MAXIMUM_CONNECT_TIME = 160, + MQTTREASONCODE_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 161, + MQTTREASONCODE_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 162 +}; + +#if defined(WIN32) || defined(WIN64) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#else + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#endif + +/** + * Returns a printable string description of an MQTT V5 reason code. + * @param value an MQTT V5 reason code. + * @return the printable string description of the input reason code. + * NULL if the code was not found. + */ +DLLExport const char* MQTTReasonCode_toString(enum MQTTReasonCodes value); + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTSubscribe.h b/src/remote/include/paho_mqtt_3c/MQTTSubscribe.h new file mode 100644 index 0000000..aa91826 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTSubscribe.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 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 + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTSUBSCRIBE_H_ +#define MQTTSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[], int requestedQoSs[]); + +DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, + int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); + +DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); + +DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len); + + +#endif /* MQTTSUBSCRIBE_H_ */ diff --git a/src/remote/include/paho_mqtt_3c/MQTTSubscribeOpts.h b/src/remote/include/paho_mqtt_3c/MQTTSubscribeOpts.h new file mode 100644 index 0000000..1ae4678 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTSubscribeOpts.h @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#if !defined(SUBOPTS_H) +#define SUBOPTS_H + +/** The MQTT V5 subscribe options, apart from QoS which existed before V5. */ +typedef struct MQTTSubscribe_options +{ + /** The eyecatcher for this structure. Must be MQSO. */ + char struct_id[4]; + /** The version number of this structure. Must be 0. + */ + int struct_version; + /** To not receive our own publications, set to 1. + * 0 is the original MQTT behaviour - all messages matching the subscription are received. + */ + unsigned char noLocal; + /** To keep the retain flag as on the original publish message, set to 1. + * If 0, defaults to the original MQTT behaviour where the retain flag is only set on + * publications sent by a broker if in response to a subscribe request. + */ + unsigned char retainAsPublished; + /** 0 - send retained messages at the time of the subscribe (original MQTT behaviour) + * 1 - send retained messages on subscribe only if the subscription is new + * 2 - do not send retained messages at all + */ + unsigned char retainHandling; +} MQTTSubscribe_options; + +#define MQTTSubscribe_options_initializer { {'M', 'Q', 'S', 'O'}, 0, 0, 0, 0 } + +#endif diff --git a/src/remote/include/paho_mqtt_3c/MQTTUnsubscribe.h b/src/remote/include/paho_mqtt_3c/MQTTUnsubscribe.h new file mode 100644 index 0000000..355ca9a --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/MQTTUnsubscribe.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 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 + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTUNSUBSCRIBE_H_ +#define MQTTUNSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]); + +DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], + unsigned char* buf, int len); + +DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); + +DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); + +#endif /* MQTTUNSUBSCRIBE_H_ */ diff --git a/src/remote/include/paho_mqtt_3c/Messages.h b/src/remote/include/paho_mqtt_3c/Messages.h new file mode 100644 index 0000000..08f292f --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 "Log.h" + +const char* Messages_get(int, enum LOG_LEVELS); + +#endif diff --git a/src/remote/include/paho_mqtt_3c/OsWrapper.h b/src/remote/include/paho_mqtt_3c/OsWrapper.h new file mode 100644 index 0000000..f657ab1 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 + +#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/src/remote/include/paho_mqtt_3c/SHA1.h b/src/remote/include/paho_mqtt_3c/SHA1.h new file mode 100644 index 0000000..f8e8abd --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/SHA1.h @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2018 Wind River Systems, Inc. All Rights Reserved. + * + * 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: + * Keith Holman - initial implementation and documentation + *******************************************************************************/ + +#if !defined(SHA1_H) +#define SHA1_H + +#if defined(OPENSSL) +#include + +/** SHA-1 Digest Length */ +#define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH + +#else /* if defined(OPENSSL) */ + +#if defined(WIN32) || defined(WIN64) +#include +#include +typedef struct SHA_CTX_S +{ + HCRYPTPROV hProv; + HCRYPTHASH hHash; +} SHA_CTX; +#else /* if defined(WIN32) || defined(WIN64) */ + +#include +typedef struct SHA_CTX_S { + uint32_t h[5]; + union { + uint32_t w[16]; + uint8_t buffer[64]; + }; + unsigned int size; + unsigned int total; +} SHA_CTX; +#endif /* else if defined(WIN32) || defined(WIN64) */ + +#include + +/** SHA-1 Digest Length (number of bytes in SHA1) */ +#define SHA1_DIGEST_LENGTH (160/8) + +/** + * Initializes the SHA1 hashing algorithm + * + * @param[in,out] ctx hashing context structure + * + * @see SHA1_Update + * @see SHA1_Final + */ +int SHA1_Init(SHA_CTX *ctx); + +/** + * Updates a block to the SHA1 hash + * + * @param[in,out] ctx hashing context structure + * @param[in] data block of data to hash + * @param[in] len length of block to hash + * + * @see SHA1_Init + * @see SHA1_Final + */ +int SHA1_Update(SHA_CTX *ctx, const void *data, size_t len); + +/** + * Produce final SHA1 hash + * + * @param[out] md SHA1 hash produced (must be atleast + * @p SHA1_DIGEST_LENGTH in length) + * @param[in,out] ctx hashing context structure + * + * @see SHA1_Init + * @see SHA1_Final + */ +int SHA1_Final(unsigned char *md, SHA_CTX *ctx); + +#endif /* if defined(OPENSSL) */ +#endif /* SHA1_H */ + diff --git a/src/remote/include/paho_mqtt_3c/SSLSocket.h b/src/remote/include/paho_mqtt_3c/SSLSocket.h new file mode 100644 index 0000000..09547fd --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 + #include + #define ssl_mutex_type pthread_mutex_t +#endif + +#include +#include "SocketBuffer.h" +#include "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, const char* hostname, size_t hostname_len); + +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, const char* hostname, int verify, int (*cb)(const char *str, size_t len, void *u), void* u); + +int SSLSocket_getPendingRead(void); +int SSLSocket_continueWrite(pending_writes* pw); + +#endif diff --git a/src/remote/include/paho_mqtt_3c/Socket.h b/src/remote/include/paho_mqtt_3c/Socket.h new file mode 100644 index 0000000..e042069 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 + +#if defined(WIN32) || defined(WIN64) +#include +#include +#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 +#if !defined(_WRS_KERNEL) +#include +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define ULONG size_t +#endif + +#include "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 "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(const char* addr, size_t addr_len, 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/src/remote/include/paho_mqtt_3c/SocketBuffer.h b/src/remote/include/paho_mqtt_3c/SocketBuffer.h new file mode 100644 index 0000000..f7702dc --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 +#else +#include +#endif + +#if defined(OPENSSL) +#include +#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/src/remote/include/paho_mqtt_3c/StackTrace.h b/src/remote/include/paho_mqtt_3c/StackTrace.h new file mode 100644 index 0000000..21a0a64 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/StackTrace.h @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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 +#include "Log.h" +#include "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/src/remote/include/paho_mqtt_3c/Thread.h b/src/remote/include/paho_mqtt_3c/Thread.h new file mode 100644 index 0000000..88fd008 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/Thread.h @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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 + * Ian Craggs, Allan Stockdill-Mander - async client updates + * Ian Craggs - fix for bug #420851 + * Ian Craggs - change MacOS semaphore implementation + *******************************************************************************/ +#include "MQTTClient.h" + +#if !defined(THREAD_H) +#define THREAD_H + +#include "mutex_type.h" /* Needed for mutex_type */ + +#if defined(WIN32) || defined(WIN64) + #include + #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 + #undef ETIMEDOUT + #define ETIMEDOUT WSAETIMEDOUT +#else + #include + + #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 + typedef dispatch_semaphore_t sem_type; + #else + #include + 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/src/remote/include/paho_mqtt_3c/Tree.h b/src/remote/include/paho_mqtt_3c/Tree.h new file mode 100644 index 0000000..bbbd014 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 /* 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/src/remote/include/paho_mqtt_3c/VersionInfo.h.in b/src/remote/include/paho_mqtt_3c/VersionInfo.h.in new file mode 100644 index 0000000..5b91bf3 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/VersionInfo.h.in @@ -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/src/remote/include/paho_mqtt_3c/WebSocket.h b/src/remote/include/paho_mqtt_3c/WebSocket.h new file mode 100644 index 0000000..33cc844 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/WebSocket.h @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2018 Wind River Systems, Inc. All Rights Reserved. + * + * 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: + * Keith Holman - initial implementation and documentation + *******************************************************************************/ + +#if !defined(WEBSOCKET_H) +#define WEBSOCKET_H + +#include "Clients.h" + +/** + * WebSocket op codes + * @{ + */ +#define WebSocket_OP_CONTINUE 0x0 /* 0000 - continue frame */ +#define WebSocket_OP_TEXT 0x1 /* 0001 - text frame */ +#define WebSocket_OP_BINARY 0x2 /* 0010 - binary frame */ +#define WebSocket_OP_CLOSE 0x8 /* 1000 - close frame */ +#define WebSocket_OP_PING 0x9 /* 1001 - ping frame */ +#define WebSocket_OP_PONG 0xA /* 1010 - pong frame */ +/** @} */ + +/** + * Various close status codes + * @{ + */ +#define WebSocket_CLOSE_NORMAL 1000 +#define WebSocket_CLOSE_GOING_AWAY 1001 +#define WebSocket_CLOSE_PROTOCOL_ERROR 1002 +#define WebSocket_CLOSE_UNKNOWN_DATA 1003 +#define WebSocket_CLOSE_RESERVED 1004 +#define WebSocket_CLOSE_NO_STATUS_CODE 1005 /* reserved: not to be used */ +#define WebSocket_CLOSE_ABNORMAL 1006 /* reserved: not to be used */ +#define WebSocket_CLOSE_BAD_DATA 1007 +#define WebSocket_CLOSE_POLICY 1008 +#define WebSocket_CLOSE_MSG_TOO_BIG 1009 +#define WebSocket_CLOSE_NO_EXTENSION 1010 +#define WebScoket_CLOSE_UNEXPECTED 1011 +#define WebSocket_CLOSE_TLS_FAIL 1015 /* reserved: not be used */ +/** @} */ + +/* closes a websocket connection */ +void WebSocket_close(networkHandles *net, int status_code, const char *reason); + +/* sends upgrade request */ +int WebSocket_connect(networkHandles *net, const char *uri); + +/* calculates the extra data required in a packet to hold a WebSocket frame header */ +size_t WebSocket_calculateFrameHeaderSize(networkHandles *net, int mask_data, + size_t data_len); + +/* obtain data from network socket */ +int WebSocket_getch(networkHandles *net, char* c); +char *WebSocket_getdata(networkHandles *net, size_t bytes, size_t* actual_len); + +/* send data out, in websocket format only if required */ +int WebSocket_putdatas(networkHandles* net, char* buf0, size_t buf0len, + int count, char** buffers, size_t* buflens, int* freeData); + +/* releases any resources used by the websocket system */ +void WebSocket_terminate(void); + +/* handles websocket upgrade request */ +int WebSocket_upgrade(networkHandles *net); + +#endif /* WEBSOCKET_H */ diff --git a/src/remote/include/paho_mqtt_3c/mutex_type.h b/src/remote/include/paho_mqtt_3c/mutex_type.h new file mode 100644 index 0000000..5760b37 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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 + #define mutex_type HANDLE +#else + #include + #define mutex_type pthread_mutex_t* +#endif + +#endif /* _MUTEX_TYPE_H_ */ diff --git a/src/remote/include/paho_mqtt_3c/pubsub_opts.h b/src/remote/include/paho_mqtt_3c/pubsub_opts.h new file mode 100644 index 0000000..a506a68 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/pubsub_opts.h @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2012, 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 contribution + * Guilherme Maciel Ferreira - add keep alive option + *******************************************************************************/ + +#if !defined(PUBSUB_OPTS_H) +#define PUBSUB_OPTS_H + +#include "MQTTAsync.h" +#include "MQTTClientPersistence.h" + +struct pubsub_opts +{ + /* debug app options */ + int publisher; /* publisher app? */ + int quiet; + int verbose; + int tracelevel; + char* delimiter; + int maxdatalen; + /* message options */ + char* message; + char* filename; + int stdin_lines; + int stdlin_complete; + int null_message; + /* MQTT options */ + int MQTTVersion; + char* topic; + char* clientid; + int qos; + int retained; + char* username; + char* password; + char* host; + char* port; + char* connection; + int keepalive; + /* will options */ + char* will_topic; + char* will_payload; + int will_qos; + int will_retain; + /* TLS options */ + int insecure; + char* capath; + char* cert; + char* cafile; + char* key; + char* keypass; + char* ciphers; + /* MQTT V5 options */ + int message_expiry; + struct { + char *name; + char *value; + } user_property; +}; + +typedef struct +{ + const char* name; + const char* value; +} pubsub_opts_nameValue; + +//void usage(struct pubsub_opts* opts, const char* version, const char* program_name); +void usage(struct pubsub_opts* opts, pubsub_opts_nameValue* name_values, const char* program_name); +int getopts(int argc, char** argv, struct pubsub_opts* opts); +char* readfile(int* data_len, struct pubsub_opts* opts); +void logProperties(MQTTProperties *props); + +#endif + + diff --git a/src/remote/include/paho_mqtt_3c/utf-8.h b/src/remote/include/paho_mqtt_3c/utf-8.h new file mode 100644 index 0000000..8bce4b3 --- /dev/null +++ b/src/remote/include/paho_mqtt_3c/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/src/remote/include/remote/json.h b/src/remote/include/remote/json.h new file mode 100644 index 0000000..d95fe6e --- /dev/null +++ b/src/remote/include/remote/json.h @@ -0,0 +1,2351 @@ +/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_AMALGAMATED_H_INCLUDED +# define JSON_AMALGAMATED_H_INCLUDED +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + +#ifndef JSON_VERSION_H_INCLUDED +#define JSON_VERSION_H_INCLUDED + +// Note: version must be updated in three places when doing a release. This +// annoying process ensures that amalgamate, CMake, and meson all report the +// correct version. +// 1. /meson.build +// 2. /include/json/version.h +// 3. /CMakeLists.txt +// IMPORTANT: also update the SOVERSION!! + +#define JSONCPP_VERSION_STRING "1.9.5" +#define JSONCPP_VERSION_MAJOR 1 +#define JSONCPP_VERSION_MINOR 9 +#define JSONCPP_VERSION_PATCH 5 +#define JSONCPP_VERSION_QUALIFIER +#define JSONCPP_VERSION_HEXA \ + ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ + (JSONCPP_VERSION_PATCH << 8)) + +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 0 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + +#endif // JSON_VERSION_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_ALLOCATOR_H_INCLUDED +#define JSON_ALLOCATOR_H_INCLUDED + +#include +#include + +#pragma pack(push) +#pragma pack() + +namespace Json { +template class SecureAllocator { +public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + */ + void deallocate(pointer p, size_type n) { + // memset_s is used because memset may be optimized away by the compiler + memset_s(p, n * sizeof(T), 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast(p)) T(std::forward(args)...); + } + + size_type max_size() const { return size_t(-1) / sizeof(T); } + + pointer address(reference x) const { return std::addressof(x); } + + const_pointer address(const_reference x) const { return std::addressof(x); } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template SecureAllocator(const SecureAllocator&) {} + template struct rebind { using other = SecureAllocator; }; +}; + +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + +} // namespace Json + +#pragma pack(pop) + +#endif // JSON_ALLOCATOR_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED +#include +#include +#include +#include +#include +#include +#include +#include + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +// Temporary, tracked for removal with issue #982. +#ifndef JSON_USE_NULLREF +#define JSON_USE_NULLREF 1 +#endif + +/// If defined, indicates that the source file is amalgamated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgamated header. +// #define JSON_IS_AMALGAMATION + +// Export macros for DLL visibility +#if defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#elif defined(__GNUC__) || defined(__clang__) +#define JSON_API __attribute__((visibility("default"))) +#endif // if defined(_MSC_VER) + +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_DLL_BUILD + +#if !defined(JSON_API) +#define JSON_API +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1800 +#error \ + "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +// As recommended at +// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...); +#define jsoncpp_snprintf msvc_pre1900_c99_snprintf +#else +#define jsoncpp_snprintf std::snprintf +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. +// C++11 should be used directly in JSONCPP. +#define JSONCPP_OVERRIDE override + +#ifdef __clang__ +#if __has_extension(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#endif +#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif // GNUC version +#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates + // MSVC) +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif // __clang__ || __GNUC__ || _MSC_VER + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +#include "allocator.h" +#include "version.h" + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +using Int = int; +using UInt = unsigned int; +#if defined(JSON_NO_INT64) +using LargestInt = int; +using LargestUInt = unsigned int; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +using Int64 = __int64; +using UInt64 = unsigned __int64; +#else // if defined(_MSC_VER) // Other platforms, use long long +using Int64 = int64_t; +using UInt64 = uint64_t; +#endif // if defined(_MSC_VER) +using LargestInt = Int64; +using LargestUInt = UInt64; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) + +template +using Allocator = + typename std::conditional, + std::allocator>::type; +using String = std::basic_string, Allocator>; +using IStringStream = + std::basic_istringstream; +using OStringStream = + std::basic_ostringstream; +using IStream = std::istream; +using OStream = std::ostream; +} // namespace Json + +// Legacy names (formerly macros). +using JSONCPP_STRING = Json::String; +using JSONCPP_ISTRINGSTREAM = Json::IStringStream; +using JSONCPP_OSTRINGSTREAM = Json::OStringStream; +using JSONCPP_ISTREAM = Json::IStream; +using JSONCPP_OSTREAM = Json::OStream; + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class StreamWriter; +class StreamWriterBuilder; +class Writer; +class FastWriter; +class StyledWriter; +class StyledStreamWriter; + +// reader.h +class Reader; +class CharReader; +class CharReaderBuilder; + +// json_features.h +class Features; + +// value.h +using ArrayIndex = unsigned int; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/json_features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FEATURES_H_INCLUDED +#define JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +#pragma pack(push) +#pragma pack() + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_{true}; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_{false}; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_{false}; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_{false}; +}; + +} // namespace Json + +#pragma pack(pop) + +#endif // JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/json_features.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_H_INCLUDED +#define JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +// Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +#if defined(_MSC_VER) && _MSC_VER == 1800 +#define JSONCPP_NORETURN __declspec(noreturn) +#else +#define JSONCPP_NORETURN [[noreturn]] +#endif +#endif + +// Support for '= delete' with template declarations was a late addition +// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2 +// even though these declare themselves to be c++11 compilers. +#if !defined(JSONCPP_TEMPLATE_DELETE) +#if defined(__clang__) && defined(__apple_build_version__) +#if __apple_build_version__ <= 8000042 +#define JSONCPP_TEMPLATE_DELETE +#endif +#elif defined(__clang__) +#if __clang_major__ == 3 && __clang_minor__ <= 8 +#define JSONCPP_TEMPLATE_DELETE +#endif +#endif +#if !defined(JSONCPP_TEMPLATE_DELETE) +#define JSONCPP_TEMPLATE_DELETE = delete +#endif +#endif + +#include +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251 4275) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push) +#pragma pack() + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +#if JSON_USE_EXCEPTION +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(String msg); + ~Exception() noexcept override; + char const* what() const noexcept override; + +protected: + String msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(String const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(String const& msg); +}; +#endif + +/// used internally +JSONCPP_NORETURN void throwRuntimeError(String const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(String const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +/** \brief Type of precision for formatting of real values. + */ +enum PrecisionType { + significantDigits = 0, ///< we set max number of significant digits in string + decimalPlaces ///< we set max number of digits after "." in string +}; + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignment takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of member keys of an object using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; + +public: + using Members = std::vector; + using iterator = ValueIterator; + using const_iterator = ValueConstIterator; + using UInt = Json::UInt; + using Int = Json::Int; +#if defined(JSON_HAS_INT64) + using UInt64 = Json::UInt64; + using Int64 = Json::Int64; +#endif // defined(JSON_HAS_INT64) + using LargestInt = Json::LargestInt; + using LargestUInt = Json::LargestUInt; + using ArrayIndex = Json::ArrayIndex; + + // Required for boost integration, e. g. BOOST_TEST + using value_type = std::string; + +#if JSON_USE_NULLREF + // Binary compatibility kludges, do not use. + static const Value& null; + static const Value& nullRef; +#endif + + // null and nullRef are deprecated, use this instead. + static Value const& nullSingleton(); + + /// Minimum signed integer value that can be stored in a Json::Value. + static constexpr LargestInt minLargestInt = + LargestInt(~(LargestUInt(-1) / 2)); + /// Maximum signed integer value that can be stored in a Json::Value. + static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2); + /// Maximum unsigned integer value that can be stored in a Json::Value. + static constexpr LargestUInt maxLargestUInt = LargestUInt(-1); + + /// Minimum signed int value that can be stored in a Json::Value. + static constexpr Int minInt = Int(~(UInt(-1) / 2)); + /// Maximum signed int value that can be stored in a Json::Value. + static constexpr Int maxInt = Int(UInt(-1) / 2); + /// Maximum unsigned int value that can be stored in a Json::Value. + static constexpr UInt maxUInt = UInt(-1); + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2)); + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2); + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static constexpr UInt64 maxUInt64 = UInt64(-1); +#endif // defined(JSON_HAS_INT64) + /// Default precision for real value for string representation. + static constexpr UInt defaultRealPrecision = 17; + // The constant is hard-coded because some compiler have trouble + // converting Value::maxUInt64 to a double correctly (AIX/xlC). + // Assumes that UInt64 is a 64 bits integer. + static constexpr double maxUInt64AsDouble = 18446744073709551615.0; +// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler +// when using gcc and clang backend compilers. CZString +// cannot be defined as private. See issue #486 +#ifdef __NVCC__ +public: +#else +private: +#endif +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); + CZString(CZString&& other) noexcept; + ~CZString(); + CZString& operator=(const CZString& other); + CZString& operator=(CZString&& other) noexcept; + + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + // const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_ : 2; + unsigned length_ : 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: + typedef std::map ObjectValues; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** + * \brief Create a default Value of the given type. + * + * This is a very useful constructor. + * To create an empty array, pass arrayValue. + * To create an empty object, pass objectValue. + * Another Value can then be set to this one by assignment. + * This is useful since clear() and resize() will not alter types. + * + * Examples: + * \code + * Json::Value null_value; // null + * Json::Value arr_value(Json::arrayValue); // [] + * Json::Value obj_value(Json::objectValue); // {} + * \endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** + * \brief Constructs a value from a static string. + * + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to + * this constructor. + * + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, which might be + * computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const String& value); + Value(bool value); + Value(std::nullptr_t ptr) = delete; + Value(const Value& other); + Value(Value&& other) noexcept; + ~Value(); + + /// \note Overwrite existing comments. To preserve comments, use + /// #swapPayload(). + Value& operator=(const Value& other); + Value& operator=(Value&& other) noexcept; + + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + /// copy everything. + void copy(const Value& other); + /// copy values but leave comments and source offsets in place. + void copyPayload(const Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! +#if JSONCPP_USING_SECURE_MEMORY + unsigned getCStringLength() const; // Allows you to understand the length of + // the CString +#endif + String asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString(char const** begin, char const** end) const; + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + /// The `as` and `is` member function templates and specializations. + template T as() const JSONCPP_TEMPLATE_DELETE; + template bool is() const JSONCPP_TEMPLATE_DELETE; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return !isNull() + explicit operator bool() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to newSize elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex newSize); + + ///@{ + /// Access an array element (zero based index). If the array contains less + /// than index element, then null value are inserted in the array so that + /// its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + Value& operator[](int index); + ///@} + + ///@{ + /// Access an array element (zero based index). + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + const Value& operator[](int index) const; + ///@} + + /// If the array contains at least index+1 elements, returns the element + /// value, otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + Value& append(Value&& value); + + /// \brief Insert value in array at specific index + bool insert(ArrayIndex index, const Value& newValue); + bool insert(ArrayIndex index, Value&& newValue); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const String& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const String& key) const; + /** \brief Access an object value by name, create a null member if it does not + * exist. + * + * If the object has no entry for that name, then the member name used to + * store the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, + const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const String& key, const Value& defaultValue) const; + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + void removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + void removeMember(const String& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + * + * Update 'removed' iff removed. + * \param key may contain embedded nulls. + * \return true iff removed (no exceptions) + */ + bool removeMember(String const& key, Value* removed); + /// Same as removeMember(String const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + * + * O(n) expensive operations. + * Update 'removed' iff removed. + * \return true if removed (no exceptions) + */ + bool removeIndex(ArrayIndex index, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const String& key) const; + /// Same as isMember(String const& key)const + bool isMember(const char* begin, const char* end) const; + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(String const&) instead.") + void setComment(const char* comment, CommentPlacement placement) { + setComment(String(comment, strlen(comment)), placement); + } + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement) { + setComment(String(comment, len), placement); + } + /// Comments must be //... or /* ... */ + void setComment(String comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + String getComment(CommentPlacement placement) const; + + String toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(ptrdiff_t start); + void setOffsetLimit(ptrdiff_t limit); + ptrdiff_t getOffsetStart() const; + ptrdiff_t getOffsetLimit() const; + +private: + void setType(ValueType v) { + bits_.value_type_ = static_cast(v); + } + bool isAllocated() const { return bits_.allocated_; } + void setIsAllocated(bool v) { bits_.allocated_ = v; } + + void initBasic(ValueType type, bool allocated = false); + void dupPayload(const Value& other); + void releasePayload(); + void dupMeta(const Value& other); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // if allocated_, ptr to { unsigned, char[] }. + ObjectValues* map_; + } value_; + + struct { + // Really a ValueType, but types should agree for bitfield packing. + unsigned int value_type_ : 8; + // Unless allocated_, string_ must be null-terminated. + unsigned int allocated_ : 1; + } bits_; + + class Comments { + public: + Comments() = default; + Comments(const Comments& that); + Comments(Comments&& that) noexcept; + Comments& operator=(const Comments& that); + Comments& operator=(Comments&& that) noexcept; + bool has(CommentPlacement slot) const; + String get(CommentPlacement slot) const; + void set(CommentPlacement slot, String comment); + + private: + using Array = std::array; + std::unique_ptr ptr_; + }; + Comments comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + ptrdiff_t start_; + ptrdiff_t limit_; +}; + +template <> inline bool Value::as() const { return asBool(); } +template <> inline bool Value::is() const { return isBool(); } + +template <> inline Int Value::as() const { return asInt(); } +template <> inline bool Value::is() const { return isInt(); } + +template <> inline UInt Value::as() const { return asUInt(); } +template <> inline bool Value::is() const { return isUInt(); } + +#if defined(JSON_HAS_INT64) +template <> inline Int64 Value::as() const { return asInt64(); } +template <> inline bool Value::is() const { return isInt64(); } + +template <> inline UInt64 Value::as() const { return asUInt64(); } +template <> inline bool Value::is() const { return isUInt64(); } +#endif + +template <> inline double Value::as() const { return asDouble(); } +template <> inline bool Value::is() const { return isDouble(); } + +template <> inline String Value::as() const { return asString(); } +template <> inline bool Value::is() const { return isString(); } + +/// These `as` specializations are type conversions, and do not have a +/// corresponding `is`. +template <> inline float Value::as() const { return asFloat(); } +template <> inline const char* Value::as() const { + return asCString(); +} + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(String key); + +private: + enum Kind { kindNone = 0, kindIndex, kindKey }; + String key_; + ArrayIndex index_{}; + Kind kind_{kindNone}; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provided as parameter + */ +class JSON_API Path { +public: + Path(const String& path, const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + using InArgs = std::vector; + using Args = std::vector; + + void makePath(const String& path, const InArgs& in); + void addPathInArg(const String& path, const InArgs& in, + InArgs::const_iterator& itInArg, PathArgument::Kind kind); + static void invalidPath(const String& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + using iterator_category = std::bidirectional_iterator_tag; + using size_t = unsigned int; + using difference_type = int; + using SelfType = ValueIteratorBase; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an + /// arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + String name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be + /// embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + /*! Internal utility functions to assist with implementing + * other iterator functions. The const and non-const versions + * of the "deref" protected methods expose the protected + * current_ member variable in a way that can often be + * optimized away by the compiler. + */ + const Value& deref() const; + Value& deref(); + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_{true}; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + using value_type = const Value; + // typedef unsigned int size_t; + // typedef int difference_type; + using reference = const Value&; + using pointer = const Value*; + using SelfType = ValueConstIterator; + + ValueConstIterator(); + ValueConstIterator(ValueIterator const& other); + +private: + /*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); + +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + using value_type = Value; + using size_t = unsigned int; + using difference_type = int; + using reference = Value&; + using pointer = Value*; + using SelfType = ValueIterator; + + ValueIterator(); + explicit ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: + /*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); + +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + /*! The return value of non-const iterators can be + * changed, so the these functions are not const + * because the returned references/pointers can be used + * to change state of the base class. + */ + reference operator*() const { return const_cast(deref()); } + pointer operator->() const { return const_cast(&deref()); } +}; + +inline void swap(Value& a, Value& b) { a.swap(b); } + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_READER_H_INCLUDED +#define JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "json_features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push) +#pragma pack() + +namespace Json { + +/** \brief Unserialize a JSON document into a + * Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ + +class JSON_API Reader { +public: + using Char = char; + using Location = const Char*; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. + */ + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + + /** \brief Constructs a Reader allowing all features for parsing. + * \deprecated Use CharReader and CharReaderBuilder. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set for parsing. + * \deprecated Use CharReader and CharReaderBuilder. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * + * \param document UTF-8 encoded string containing the document + * to read. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool parse(const std::string& document, Value& root, + bool collectComments = true); + + /** \brief Read a Value from a JSON + * document. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded + * string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string + * of the document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(IStream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + String getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. + */ + String getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured errors encountered while parsing. + * + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate multiple + * errors. This can occur if the parser recovers from a non-fatal parse + * error and then encounters additional errors. + */ + std::vector getStructuredErrors() const; + + /** \brief Add a semantic error message. + * + * \param value JSON Value location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the Value + * offset exceeds the document size. + */ + bool pushError(const Value& value, const String& message); + + /** \brief Add a semantic error message with extra context. + * + * \param value JSON Value location associated with the error + * \param message The error message. + * \param extra Additional JSON Value location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const String& message, const Value& extra); + + /** \brief Return whether there are any errors. + * + * \return \c true if there are no errors to report \c false if errors have + * occurred. + */ + bool good() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + String message_; + Location extra_; + }; + + using Errors = std::deque; + + bool readToken(Token& token); + void skipSpaces(); + bool match(const Char* pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, String& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static bool containsNewLine(Location begin, Location end); + static String normalizeEOL(Location begin, Location end); + + using Nodes = std::stack; + Nodes nodes_; + Errors errors_; + String document_; + Location begin_{}; + Location end_{}; + Location current_{}; + Location lastValueEnd_{}; + Value* lastValue_{}; + String commentsBefore_; + Features features_; + bool collectComments_{}; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() = default; + /** \brief Read a Value from a JSON + * document. The document must be a UTF-8 encoded string containing the + * document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string + * of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + * document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it was + * successfully parsed. + * \param[out] errs Formatted error messages (if not NULL) a user + * friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) = 0; + + class JSON_API Factory { + public: + virtual ~Factory() = default; + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + * + * Usage: + * \code + * using namespace Json; + * CharReaderBuilder builder; + * builder["collectComments"] = false; + * Value value; + * String errs; + * bool ok = parseFromStream(builder, std::cin, &value, &errs); + * \endcode + */ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + * These are case-sensitive. + * Available settings (case-sensitive): + * - `"collectComments": false or true` + * - true to collect comment and allow writing them back during + * serialization, false to discard comments. This parameter is ignored + * if allowComments is false. + * - `"allowComments": false or true` + * - true if comments are allowed. + * - `"allowTrailingCommas": false or true` + * - true if trailing commas in objects and arrays are allowed. + * - `"strictRoot": false or true` + * - true if root must be either an array or an object value + * - `"allowDroppedNullPlaceholders": false or true` + * - true if dropped null placeholders are allowed. (See + * StreamWriterBuilder.) + * - `"allowNumericKeys": false or true` + * - true if numeric object keys are allowed. + * - `"allowSingleQuotes": false or true` + * - true if '' are allowed for strings (both keys and values) + * - `"stackLimit": integer` + * - Exceeding stackLimit (recursive depth of `readValue()`) will cause an + * exception. + * - This is a security issue (seg-faults caused by deeply nested JSON), so + * the default is low. + * - `"failIfExtra": false or true` + * - If true, `parse()` returns false when extra non-whitespace trails the + * JSON value in the input string. + * - `"rejectDupKeys": false or true` + * - If true, `parse()` returns false when a key is duplicated within an + * object. + * - `"allowSpecialFloats": false or true` + * - If true, special float values (NaNs and infinities) are allowed and + * their values are lossfree restorable. + * - `"skipBom": false or true` + * - If true, if the input starts with the Unicode byte order mark (BOM), + * it is skipped. + * + * You can examine 'settings_` yourself to see the defaults. You can also + * write and read them just like any JSON Value. + * \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + ~CharReaderBuilder() override; + + CharReader* newCharReader() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](const String& key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root, + String* errs); + +/** \brief Read from 'sin' into 'root'. + * + * Always keep comments from the input JSON. + * + * This can be used to read a file into a particular sub-object. + * For example: + * \code + * Json::Value root; + * cin >> root["dir"]["file"]; + * cout << root; + * \endcode + * Result: + * \verbatim + * { + * "dir": { + * "file": { + * // The input stream JSON would be nested here. + * } + * } + * } + * \endverbatim + * \throw std::exception on parse error. + * \see Json::operator<<() + */ +JSON_API IStream& operator>>(IStream&, Value&); + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#pragma pack(push) +#pragma pack() + +namespace Json { + +class Value; + +/** + * + * Usage: + * \code + * using namespace Json; + * void writeToStdout(StreamWriter::Factory const& factory, Value const& value) + * { std::unique_ptr const writer( factory.newStreamWriter()); + * writer->write(value, &std::cout); + * std::cout << std::endl; // add lf and flush + * } + * \endcode + */ +class JSON_API StreamWriter { +protected: + OStream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + * Do not take ownership of sout, but maintain a reference during function. + * \pre sout != NULL + * \return zero on success (For now, we always return zero, so check the + * stream instead.) \throw std::exception possibly, depending on + * configuration + */ + virtual int write(Value const& root, OStream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +String JSON_API writeString(StreamWriter::Factory const& factory, + Value const& root); + +/** \brief Build a StreamWriter implementation. + +* Usage: +* \code +* using namespace Json; +* Value value = ...; +* StreamWriterBuilder builder; +* builder["commentStyle"] = "None"; +* builder["indentation"] = " "; // or whatever you like +* std::unique_ptr writer( +* builder.newStreamWriter()); +* writer->write(value, &std::cout); +* std::cout << std::endl; // add lf and flush +* \endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + * Available settings (case-sensitive): + * - "commentStyle": "None" or "All" + * - "indentation": "". + * - Setting this to an empty string also omits newline characters. + * - "enableYAMLCompatibility": false or true + * - slightly change the whitespace around colons + * - "dropNullPlaceholders": false or true + * - Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + * - "useSpecialFloats": false or true + * - If true, outputs non-finite floating point values in the following way: + * NaN values as "NaN", positive infinity as "Infinity", and negative + * infinity as "-Infinity". + * - "precision": int + * - Number of precision digits for formatting of real values. + * - "precisionType": "significant"(default) or "decimal" + * - Type of precision for formatting of real values. + * - "emitUTF8": false or true + * - If true, outputs raw UTF8 strings instead of escaping them. + + * You can examine 'settings_` yourself + * to see the defaults. You can also write and read them just like any + * JSON Value. + * \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + ~StreamWriterBuilder() override; + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + StreamWriter* newStreamWriter() const override; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](const String& key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual String write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be useful to support feature such as RPC where bandwidth is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSON_API FastWriter + : public Writer { +public: + FastWriter(); + ~FastWriter() override = default; + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + String write(const Value& root) override; + +private: + void writeValue(const Value& value); + + String document_; + bool yamlCompatibilityEnabled_{false}; + bool dropNullPlaceholders_{false}; + bool omitEndingLineFeed_{false}; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputted according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSON_API + StyledWriter : public Writer { +public: + StyledWriter(); + ~StyledWriter() override = default; + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + String write(const Value& root) override; + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); + void writeIndent(); + void writeWithIndent(const String& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); + + using ChildValues = std::vector; + + ChildValues childValues_; + String document_; + String indentString_; + unsigned int rightMargin_{74}; + unsigned int indentSize_{3}; + bool addChildValues_{false}; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputted according to their + #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSON_API + StyledStreamWriter { +public: + /** + * \param indentation Each level will be indented by this amount extra. + */ + StyledStreamWriter(String indentation = "\t"); + ~StyledStreamWriter() = default; + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(OStream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); + void writeIndent(); + void writeWithIndent(const String& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); + + using ChildValues = std::vector; + + ChildValues childValues_; + OStream* document_; + String indentString_; + unsigned int rightMargin_{74}; + String indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(JSON_HAS_INT64) +String JSON_API valueToString(Int value); +String JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +String JSON_API valueToString(LargestInt value); +String JSON_API valueToString(LargestUInt value); +String JSON_API valueToString( + double value, unsigned int precision = Value::defaultRealPrecision, + PrecisionType precisionType = PrecisionType::significantDigits); +String JSON_API valueToString(bool value); +String JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API OStream& operator<<(OStream&, const Value& root); + +} // namespace Json + +#pragma pack(pop) + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_ASSERTIONS_H_INCLUDED +#define JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +#define JSON_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + Json::throwLogicError("assert json failed"); \ + } \ + } while (0) + +#define JSON_FAIL_MESSAGE(message) \ + do { \ + OStringStream oss; \ + oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } while (0) + +#else // JSON_USE_EXCEPTION + +#define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +#define JSON_FAIL_MESSAGE(message) \ + { \ + OStringStream oss; \ + oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + do { \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } \ + } while (0) + +#endif // JSON_ASSERTIONS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_AMALGAMATED_H_INCLUDED diff --git a/src/remote/include/remote/remote_node.hpp b/src/remote/include/remote/remote_node.hpp new file mode 100644 index 0000000..9a6a1e3 --- /dev/null +++ b/src/remote/include/remote/remote_node.hpp @@ -0,0 +1,15 @@ +#ifndef __REMOTE_NODE_H__ +#define __REMOTE_NODE_H__ + +struct alignas(8) car_ctrl +{ + int gear; // 挡位 0:N档,1:D档,2:R档 + float throttle; // 油门 0~65535 + float steering; // 转向 0~65535 + float brake; // 刹车 0~65535 + int sweep; // 清扫 1:清扫 0:不清扫 +}; + +extern car_ctrl car_ctrl_mes; + +#endif \ No newline at end of file diff --git a/src/remote/lib/libpaho-mqtt3c-static.a b/src/remote/lib/libpaho-mqtt3c-static.a new file mode 100644 index 0000000000000000000000000000000000000000..cbfe668bbd98960f14a5de00f1e56355eb5714a2 GIT binary patch literal 331658 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 B@|XYs literal 0 HcmV?d00001 diff --git a/src/remote/lib/libpaho-mqtt3c.a b/src/remote/lib/libpaho-mqtt3c.a new file mode 100644 index 0000000000000000000000000000000000000000..9f7e1d546ea4e97802a5315594654d9253c3c74c GIT binary patch literal 362166 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 V;4*64TXPB>y4*{{k{*rA+_; literal 0 HcmV?d00001 diff --git a/src/remote/package.xml b/src/remote/package.xml new file mode 100644 index 0000000..4008fb9 --- /dev/null +++ b/src/remote/package.xml @@ -0,0 +1,22 @@ + + + + remote + 0.0.0 + TODO: Package description + zxwl + TODO: License declaration + + ament_cmake + + rclcpp + std_msgs + sweeper_interfaces + + ament_lint_auto + ament_lint_common + + + ament_cmake + + \ No newline at end of file diff --git a/src/remote/src/jsoncpp.cpp b/src/remote/src/jsoncpp.cpp new file mode 100644 index 0000000..89b7bc0 --- /dev/null +++ b/src/remote/src/jsoncpp.cpp @@ -0,0 +1,5342 @@ +/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "json.h" + +#ifndef JSON_IS_AMALGAMATION +#error "Compile with -I PATH_TO_JSON_DIRECTORY" +#endif + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include +#endif + +// Also support old flag NO_LOCALE_SUPPORT +#ifdef NO_LOCALE_SUPPORT +#define JSONCPP_NO_LOCALE_SUPPORT +#endif + +#ifndef JSONCPP_NO_LOCALE_SUPPORT +#include +#endif + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { +static inline char getDecimalPoint() { +#ifdef JSONCPP_NO_LOCALE_SUPPORT + return '\0'; +#else + struct lconv* lc = localeconv(); + return lc ? *(lc->decimal_point) : '\0'; +#endif +} + +/// Converts a unicode code-point to UTF-8. +static inline String codePointToUTF8(unsigned int cp) { + String result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast(0xE0 | (0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +using UIntToStringBuffer = char[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned integer to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = static_cast(value % 10U + static_cast('0')); + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +template Iter fixNumericLocale(Iter begin, Iter end) { + for (; begin != end; ++begin) { + if (*begin == ',') { + *begin = '.'; + } + } + return begin; +} + +template void fixNumericLocaleInput(Iter begin, Iter end) { + char decimalPoint = getDecimalPoint(); + if (decimalPoint == '\0' || decimalPoint == '.') { + return; + } + for (; begin != end; ++begin) { + if (*begin == '.') { + *begin = decimalPoint; + } + } +} + +/** + * Return iterator that would be the new end of the range [begin,end), if we + * were to delete zeros in the end of string, but not the last zero before '.'. + */ +template +Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) { + for (; begin != end; --end) { + if (*(end - 1) != '0') { + return end; + } + // Don't delete the last zero before the decimal point. + if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') { + if (precision) { + return end; + } + return end - 2; + } + } + return end; +} + +} // namespace Json + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors +// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" +#include +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if __cplusplus >= 201103L + +#if !defined(sscanf) +#define sscanf std::sscanf +#endif + +#endif //__cplusplus + +#if defined(_MSC_VER) +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES +#endif //_MSC_VER + +#if defined(_MSC_VER) +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile +// time to change the stack limit +#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) +#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 +#endif + +static size_t const stackLimit_g = + JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +using CharReaderPtr = std::unique_ptr; +#else +using CharReaderPtr = std::auto_ptr; +#endif + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() = default; + +Features Features::all() { return {}; } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() : features_(Features::all()) {} + +Reader::Reader(const Features& features) : features_(features) {} + +bool Reader::parse(const std::string& document, Value& root, + bool collectComments) { + document_.assign(document.begin(), document.end()); + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& is, Value& root, bool collectComments) { + // std::istream_iterator begin(is); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since String is reference-counted, this at least does not + // create an extra copy. + String doc(std::istreambuf_iterator(is), {}); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); +} + +bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // readValue() may call itself only if it calls readObject() or ReadArray(). + // These methods execute nodes_.push() just before and nodes_.pop)() just + // after calling readValue(). parse() executes one nodes_.push(), so > instead + // of >=. + if (nodes_.size() > stackLimit_g) + throwRuntimeError("Exceeded stackLimit in readValue()."); + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenFalse: { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNull: { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return ok; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(const Char* pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + String normalized; + normalized.reserve(static_cast(end - begin)); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void Reader::addComment(Location begin, Location end, + CommentPlacement placement) { + assert(collectComments_); + const String& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != nullptr); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + Location p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } +} + +bool Reader::readString() { + Char c = '\0'; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& token) { + Token tokenName; + String name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); +} + +bool Reader::readArray(Token& token) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token currentToken; + // Accept Comment after last item in the array. + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); + } + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); + } + if (currentToken.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of + // them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + auto digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) { + if (value == std::numeric_limits::max()) + value = std::numeric_limits::infinity(); + else if (value == std::numeric_limits::lowest()) + value = -std::numeric_limits::infinity(); + else if (!std::isinf(value)) + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); + } + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + String decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, current); + if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool Reader::addError(const String& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + size_t const errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +String Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +String Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +String Reader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector Reader::getStructuredErrors() const { + std::vector allErrors; + for (const auto& error : errors_) { + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const String& message) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = nullptr; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const String& message, + const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length || + extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { return errors_.empty(); } + +// Originally copied from the Features class (now deprecated), used internally +// for features implementation. +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool allowTrailingCommas_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + bool skipBom_; + size_t stackLimit_; +}; // OurFeatures + +OurFeatures OurFeatures::all() { return {}; } + +// Implementation of class Reader +// //////////////////////////////// + +// Originally copied from the Reader class (now deprecated), used internally +// for implementing JSON reading. +class OurReader { +public: + using Char = char; + using Location = const Char*; + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + + explicit OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments = true); + String getFormattedErrorMessages() const; + std::vector getStructuredErrors() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + String message_; + Location extra_; + }; + + using Errors = std::deque; + + bool readToken(Token& token); + void skipSpaces(); + void skipBom(bool skipBom); + bool match(const Char* pattern, int patternLength); + bool readComment(); + bool readCStyleComment(bool* containsNewLineResult); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, String& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static String normalizeEOL(Location begin, Location end); + static bool containsNewLine(Location begin, Location end); + + using Nodes = std::stack; + + Nodes nodes_{}; + Errors errors_{}; + String document_{}; + Location begin_ = nullptr; + Location end_ = nullptr; + Location current_ = nullptr; + Location lastValueEnd_ = nullptr; + Value* lastValue_ = nullptr; + bool lastValueHasAComment_ = false; + String commentsBefore_{}; + + OurFeatures const features_; + bool collectComments_ = false; +}; // OurReader + +// complete copy of Read impl, for OurReader + +bool OurReader::containsNewLine(OurReader::Location begin, + OurReader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); +} + +OurReader::OurReader(OurFeatures const& features) : features_(features) {} + +bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + // skip byte order mark if it exists at the beginning of the UTF-8 text. + skipBom(features_.skipBom_); + bool successful = readValue(); + nodes_.pop(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + // To preserve the old behaviour we cast size_t to int. + if (nodes_.size() > features_.stackLimit_) + throwRuntimeError("Exceeded stackLimit in readValue()."); + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenFalse: { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNull: { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNaN: { + Value v(std::numeric_limits::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenPosInf: { + Value v(std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenNegInf: { + Value v(-std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValueHasAComment_ = false; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + } else { + // If we don't allow single quotes, this is a failure case. + ok = false; + } + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case '+': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenPosInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { + ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return ok; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +void OurReader::skipBom(bool skipBom) { + // The default behavior is to skip BOM. + if (skipBom) { + if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) { + begin_ += 3; + current_ = begin_; + } + } +} + +bool OurReader::match(const Char* pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + const Location commentBegin = current_ - 1; + const Char c = getNextChar(); + bool successful = false; + bool cStyleWithEmbeddedNewline = false; + + const bool isCStyleComment = (c == '*'); + const bool isCppStyleComment = (c == '/'); + if (isCStyleComment) { + successful = readCStyleComment(&cStyleWithEmbeddedNewline); + } else if (isCppStyleComment) { + successful = readCppStyleComment(); + } + + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + + if (!lastValueHasAComment_) { + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (isCppStyleComment || !cStyleWithEmbeddedNewline) { + placement = commentAfterOnSameLine; + lastValueHasAComment_ = true; + } + } + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +String OurReader::normalizeEOL(OurReader::Location begin, + OurReader::Location end) { + String normalized; + normalized.reserve(static_cast(end - begin)); + OurReader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void OurReader::addComment(Location begin, Location end, + CommentPlacement placement) { + assert(collectComments_); + const String& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != nullptr); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment(bool* containsNewLineResult) { + *containsNewLineResult = false; + + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + if (c == '\n') + *containsNewLineResult = true; + } + + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + Location p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& token) { + Token tokenName; + String name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && + (name.empty() || + features_.allowTrailingCommas_)) // empty object or trailing comma + return true; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + if (name.length() >= (1U << 30)) + throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + String msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover(msg, tokenName, tokenObjectEnd); + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); +} + +bool OurReader::readArray(Token& token) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(token.start_ - begin_); + int index = 0; + for (;;) { + skipSpaces(); + if (current_ != end_ && *current_ == ']' && + (index == 0 || + (features_.allowTrailingCommas_ && + !features_.allowDroppedNullPlaceholders_))) // empty array or trailing + // comma + { + Token endArray; + readToken(endArray); + return true; + } + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token currentToken; + // Accept Comment after last item in the array. + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); + } + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); + } + if (currentToken.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + const bool isNegative = *current == '-'; + if (isNegative) { + ++current; + } + + // We assume we can represent the largest and smallest integer types as + // unsigned integers with separate sign. This is only true if they can fit + // into an unsigned integer. + static_assert(Value::maxLargestInt <= Value::maxLargestUInt, + "Int must be smaller than UInt"); + + // We need to convert minLargestInt into a positive number. The easiest way + // to do this conversion is to assume our "threshold" value of minLargestInt + // divided by 10 can fit in maxLargestInt when absolute valued. This should + // be a safe assumption. + static_assert(Value::minLargestInt <= -Value::maxLargestInt, + "The absolute value of minLargestInt must be greater than or " + "equal to maxLargestInt"); + static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt, + "The absolute value of minLargestInt must be only 1 magnitude " + "larger than maxLargest Int"); + + static constexpr Value::LargestUInt positive_threshold = + Value::maxLargestUInt / 10; + static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10; + + // For the negative values, we have to be more careful. Since typically + // -Value::minLargestInt will cause an overflow, we first divide by 10 and + // then take the inverse. This assumes that minLargestInt is only a single + // power of 10 different in magnitude, which we check above. For the last + // digit, we take the modulus before negating for the same reason. + static constexpr auto negative_threshold = + Value::LargestUInt(-(Value::minLargestInt / 10)); + static constexpr auto negative_last_digit = + Value::UInt(-(Value::minLargestInt % 10)); + + const Value::LargestUInt threshold = + isNegative ? negative_threshold : positive_threshold; + const Value::UInt max_last_digit = + isNegative ? negative_last_digit : positive_last_digit; + + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + + const auto digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, meaning value == threshold, + // b) this is the last digit, or + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > max_last_digit) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + + if (isNegative) { + // We use the same magnitude assumption here, just in case. + const auto last_digit = static_cast(value % 10); + decoded = -Value::LargestInt(value / 10) * 10 - last_digit; + } else if (value <= Value::LargestUInt(Value::maxLargestInt)) { + decoded = Value::LargestInt(value); + } else { + decoded = value; + } + + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) { + if (value == std::numeric_limits::max()) + value = std::numeric_limits::infinity(); + else if (value == std::numeric_limits::lowest()) + value = -std::numeric_limits::infinity(); + else if (!std::isinf(value)) + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); + } + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + String decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, current); + if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool OurReader::addError(const String& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + size_t errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +String OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +String OurReader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector OurReader::getStructuredErrors() const { + std::vector allErrors; + for (const auto& error : errors_) { + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; + +public: + OurCharReader(bool collectComments, OurFeatures const& features) + : collectComments_(collectComments), reader_(features) {} + bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) override { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } +CharReaderBuilder::~CharReaderBuilder() = default; +CharReader* CharReaderBuilder::newCharReader() const { + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = + settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + + // Stack limit is always a size_t, so we get this as an unsigned int + // regardless of it we have 64-bit integer support enabled. + features.stackLimit_ = static_cast(settings_["stackLimit"].asUInt()); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + features.skipBom_ = settings_["skipBom"].asBool(); + return new OurCharReader(collectComments, features); +} + +bool CharReaderBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "collectComments", + "allowComments", + "allowTrailingCommas", + "strictRoot", + "allowDroppedNullPlaceholders", + "allowNumericKeys", + "allowSingleQuotes", + "stackLimit", + "failIfExtra", + "rejectDupKeys", + "allowSpecialFloats", + "skipBom", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[key] = *si; + else + return false; + } + return invalid ? invalid->empty() : true; +} + +Value& CharReaderBuilder::operator[](const String& key) { + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) { + //! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["allowTrailingCommas"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; + (*settings)["skipBom"] = true; + //! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) { + //! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["allowTrailingCommas"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; + (*settings)["skipBom"] = true; + //! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root, + String* errs) { + OStringStream ssin; + ssin << sin.rdbuf(); + String doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +IStream& operator>>(IStream& sin, Value& root) { + CharReaderBuilder b; + String errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + throwRuntimeError(errs); + } + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() : current_() {} + +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() { return current_->second; } +const Value& ValueIteratorBase::deref() const { return current_->second; } + +void ValueIteratorBase::increment() { ++current_; } + +void ValueIteratorBase::decrement() { --current_; } + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +String ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) + return String(); + return String(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = nullptr; + return nullptr; + } + *end = cname + (*current_).first.length(); + return cname; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() = default; + +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator::ValueConstIterator(ValueIterator const& other) + : ValueIteratorBase(other) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() = default; + +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); +} + +ValueIterator::ValueIterator(const ValueIterator& other) = default; + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +// Provide implementation equivalent of std::snprintf for older _MSC compilers +#if defined(_MSC_VER) && _MSC_VER < 1900 +#include +static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size, + const char* format, va_list ap) { + int count = -1; + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + return count; +} + +int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...) { + va_list ap; + va_start(ap, format); + const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + return count; +} +#endif + +// Disable warning C4702 : unreachable code +#if defined(_MSC_VER) +#pragma warning(disable : 4702) +#endif + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { +template +static std::unique_ptr cloneUnique(const std::unique_ptr& p) { + std::unique_ptr r; + if (p) { + r = std::unique_ptr(new T(*p)); + } + return r; +} + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif + +// static +Value const& Value::nullSingleton() { + static Value const nullStatic; + return nullStatic; +} + +#if JSON_USE_NULLREF +// for backwards compatibility, we'll leave these global references around, but +// DO NOT use them in JSONCPP library code any more! +// static +Value const& Value::null = Value::nullSingleton(); + +// static +Value const& Value::nullRef = Value::nullSingleton(); +#endif + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + return d >= static_cast(min) && d <= static_cast(max); +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + + static_cast(Int64(value & 1)); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, size_t length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= static_cast(Value::maxInt)) + length = Value::maxInt - 1; + + auto newString = static_cast(malloc(length + 1)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue(const char* value, + unsigned int length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= static_cast(Value::maxInt) - + sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + size_t actualLength = sizeof(length) + length + 1; + auto newString = static_cast(malloc(actualLength)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = + 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString(bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) { + if (!isPrefixed) { + *length = static_cast(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by + * duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length == 0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { free(value); } +static inline void releaseStringValue(char* value, unsigned) { free(value); } +#endif // JSONCPP_USING_SECURE_MEMORY + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +#if JSON_USE_EXCEPTION +Exception::Exception(String msg) : msg_(std::move(msg)) {} +Exception::~Exception() noexcept = default; +char const* Exception::what() const noexcept { return msg_.c_str(); } +RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} +LogicError::LogicError(String const& msg) : Exception(msg) {} +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + throw RuntimeError(msg); +} +JSONCPP_NORETURN void throwLogicError(String const& msg) { + throw LogicError(msg); +} +#else // !JSON_USE_EXCEPTION +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + std::cerr << msg << std::endl; + abort(); +} +JSONCPP_NORETURN void throwLogicError(String const& msg) { + std::cerr << msg << std::endl; + abort(); +} +#endif + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} + +Value::CZString::CZString(char const* str, unsigned length, + DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = length & 0x3FFFFFFF; +} + +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = + static_cast( + other.cstr_ + ? (static_cast(other.storage_.policy_) == + noDuplication + ? noDuplication + : duplicate) + : static_cast(other.storage_.policy_)) & + 3U; + storage_.length_ = other.storage_.length_; +} + +Value::CZString::CZString(CZString&& other) noexcept + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast(cstr_), + storage_.length_ + 1U); // +1 for null terminating + // character for sake of + // completeness but not actually + // necessary + } +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(const CZString& other) { + cstr_ = other.cstr_; + index_ = other.index_; + return *this; +} + +Value::CZString& Value::CZString::operator=(CZString&& other) noexcept { + cstr_ = other.cstr_; + index_ = other.index_; + other.cstr_ = nullptr; + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) + return index_ < other.index_; + // return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) + return true; + if (comp > 0) + return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) + return index_ == other.index_; + // return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) + return false; + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +// const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { + return storage_.policy_ == noDuplication; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType type) { + static char const emptyString[] = ""; + initBasic(type); + switch (type) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + // allocated_ == false, so this is safe. + value_.string_ = const_cast(static_cast(emptyString)); + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + JSON_ASSERT_MESSAGE(value != nullptr, + "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue( + value, static_cast(strlen(value))); +} + +Value::Value(const char* begin, const char* end) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(begin, static_cast(end - begin)); +} + +Value::Value(const String& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue( + value.data(), static_cast(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(const Value& other) { + dupPayload(other); + dupMeta(other); +} + +Value::Value(Value&& other) noexcept { + initBasic(nullValue); + swap(other); +} + +Value::~Value() { + releasePayload(); + value_.uint_ = 0; +} + +Value& Value::operator=(const Value& other) { + Value(other).swap(*this); + return *this; +} + +Value& Value::operator=(Value&& other) noexcept { + other.swap(*this); + return *this; +} + +void Value::swapPayload(Value& other) { + std::swap(bits_, other.bits_); + std::swap(value_, other.value_); +} + +void Value::copyPayload(const Value& other) { + releasePayload(); + dupPayload(other); +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +void Value::copy(const Value& other) { + copyPayload(other); + dupMeta(other); +} + +ValueType Value::type() const { + return static_cast(bits_.value_type_); +} + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type() - other.type(); + if (typeDelta) + return typeDelta < 0; + switch (type()) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return other.value_.string_ != nullptr; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) + return true; + if (comp > 0) + return false; + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + auto thisSize = value_.map_->size(); + auto otherSize = other.value_.map_->size(); + if (thisSize != otherSize) + return thisSize < otherSize; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + if (type() != other.type()) + return false; + switch (type()) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + if (this_len != other_len) + return false; + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == nullptr) + return nullptr; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return this_str; +} + +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) + return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return this_len; +} +#endif + +bool Value::getString(char const** begin, char const** end) const { + if (type() != stringValue) + return false; + if (value_.string_ == nullptr) + return false; + unsigned length; + decodePrefixedString(this->isAllocated(), this->value_.string_, &length, + begin); + *end = *begin + length; + return true; +} + +String Value::asString() const { + switch (type()) { + case nullValue: + return ""; + case stringValue: { + if (value_.string_ == nullptr) + return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return String(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +Value::Int Value::asInt() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type()) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type()) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type()) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type()) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast(integerToDouble(value_.uint_)); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0F : 0.0F; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type()) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ != 0; + case uintValue: + return value_.uint_ != 0; + case realValue: { + // According to JavaScript language zero or NaN is regarded as false + const auto value_classification = std::fpclassify(value_.real_); + return value_classification != FP_ZERO && value_classification != FP_NAN; + } + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type() == booleanValue && !value_.bool_) || + (type() == stringValue && asString().empty()) || + (type() == arrayValue && value_.map_->empty()) || + (type() == objectValue && value_.map_->empty()) || + type() == nullValue; + case intValue: + return isInt() || + (type() == realValue && InRange(value_.real_, minInt, maxInt)) || + type() == booleanValue || type() == nullValue; + case uintValue: + return isUInt() || + (type() == realValue && InRange(value_.real_, 0, maxUInt)) || + type() == booleanValue || type() == nullValue; + case realValue: + return isNumeric() || type() == booleanValue || type() == nullValue; + case booleanValue: + return isNumeric() || type() == booleanValue || type() == nullValue; + case stringValue: + return isNumeric() || type() == booleanValue || type() == stringValue || + type() == nullValue; + case arrayValue: + return type() == arrayValue || type() == nullValue; + case objectValue: + return type() == objectValue || type() == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0U; + return false; +} + +Value::operator bool() const { return !isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue || + type() == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type()) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type() == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + for (ArrayIndex i = oldSize; i < newSize; ++i) + (*this)[i]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + JSON_ASSERT(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type() == nullValue) + *this = Value(arrayValue); + CZString key(index); + auto it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type() == nullValue) + return nullSingleton(); + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullSingleton(); + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType type, bool allocated) { + setType(type); + setIsAllocated(allocated); + comments_ = Comments{}; + start_ = 0; + limit_ = 0; +} + +void Value::dupPayload(const Value& other) { + setType(other.type()); + setIsAllocated(false); + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.isAllocated()) { + unsigned len; + char const* str; + decodePrefixedString(other.isAllocated(), other.value_.string_, &len, + &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + setIsAllocated(true); + } else { + value_.string_ = other.value_.string_; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::releasePayload() { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (isAllocated()) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::dupMeta(const Value& other) { + comments_ = other.comments_; + start_ = other.start_; + limit_ = other.limit_; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type() == nullValue) + *this = Value(objectValue); + CZString actualKey(key, static_cast(strlen(key)), + CZString::noDuplication); // NOTE! + auto it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* end) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type() == nullValue) + *this = Value(objectValue); + CZString actualKey(key, static_cast(end - key), + CZString::duplicateOnCopy); + auto it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullSingleton() ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* begin, char const* end) const { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::find(begin, end): requires " + "objectValue or nullValue"); + if (type() == nullValue) + return nullptr; + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return nullptr; + return &(*it).second; +} +Value* Value::demand(char const* begin, char const* end) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::demand(begin, end): requires " + "objectValue or nullValue"); + return &resolveReference(begin, end); +} +const Value& Value::operator[](const char* key) const { + Value const* found = find(key, key + strlen(key)); + if (!found) + return nullSingleton(); + return *found; +} +Value const& Value::operator[](const String& key) const { + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) + return nullSingleton(); + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const String& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +Value& Value::append(const Value& value) { return append(Value(value)); } + +Value& Value::append(Value&& value) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::append: requires arrayValue"); + if (type() == nullValue) { + *this = Value(arrayValue); + } + return this->value_.map_->emplace(size(), std::move(value)).first->second; +} + +bool Value::insert(ArrayIndex index, const Value& newValue) { + return insert(index, Value(newValue)); +} + +bool Value::insert(ArrayIndex index, Value&& newValue) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::insert: requires arrayValue"); + ArrayIndex length = size(); + if (index > length) { + return false; + } + for (ArrayIndex i = length; i > index; i--) { + (*this)[i] = std::move((*this)[i - 1]); + } + (*this)[index] = std::move(newValue); + return true; +} + +Value Value::get(char const* begin, char const* end, + Value const& defaultValue) const { + Value const* found = find(begin, end); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const { + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(String const& key, Value const& defaultValue) const { + return get(key.data(), key.data() + key.length(), defaultValue); +} + +bool Value::removeMember(const char* begin, const char* end, Value* removed) { + if (type() != objectValue) { + return false; + } + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); + auto it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + if (removed) + *removed = std::move(it->second); + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) { + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(String const& key, Value* removed) { + return removeMember(key.data(), key.data() + key.length(), removed); +} +void Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type() == nullValue) + return; + + CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); + value_.map_->erase(actualKey); +} +void Value::removeMember(const String& key) { removeMember(key.c_str()); } + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type() != arrayValue) { + return false; + } + CZString key(index); + auto it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + if (removed) + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i) { + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + auto itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +bool Value::isMember(char const* begin, char const* end) const { + Value const* value = find(begin, end); + return nullptr != value; +} +bool Value::isMember(char const* key) const { + return isMember(key, key + strlen(key)); +} +bool Value::isMember(String const& key) const { + return isMember(key.data(), key.data() + key.length()); +} + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type() == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(String((*it).first.data(), (*it).first.length())); + } + return members; +} + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type() == nullValue; } + +bool Value::isBool() const { return type() == booleanValue; } + +bool Value::isInt() const { + switch (type()) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= minInt && value_.int_ <= maxInt; +#else + return true; +#endif + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type()) { + case intValue: +#if defined(JSON_HAS_INT64) + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); +#else + return value_.int_ >= 0; +#endif + case uintValue: +#if defined(JSON_HAS_INT64) + return value_.uint_ <= maxUInt; +#else + return true; +#endif + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type()) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type()) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { + switch (type()) { + case intValue: + case uintValue: + return true; + case realValue: +#if defined(JSON_HAS_INT64) + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); +#else + return value_.real_ >= minInt && value_.real_ <= maxUInt && + IsIntegral(value_.real_); +#endif // JSON_HAS_INT64 + default: + break; + } + return false; +} + +bool Value::isDouble() const { + return type() == intValue || type() == uintValue || type() == realValue; +} + +bool Value::isNumeric() const { return isDouble(); } + +bool Value::isString() const { return type() == stringValue; } + +bool Value::isArray() const { return type() == arrayValue; } + +bool Value::isObject() const { return type() == objectValue; } + +Value::Comments::Comments(const Comments& that) + : ptr_{cloneUnique(that.ptr_)} {} + +Value::Comments::Comments(Comments&& that) noexcept + : ptr_{std::move(that.ptr_)} {} + +Value::Comments& Value::Comments::operator=(const Comments& that) { + ptr_ = cloneUnique(that.ptr_); + return *this; +} + +Value::Comments& Value::Comments::operator=(Comments&& that) noexcept { + ptr_ = std::move(that.ptr_); + return *this; +} + +bool Value::Comments::has(CommentPlacement slot) const { + return ptr_ && !(*ptr_)[slot].empty(); +} + +String Value::Comments::get(CommentPlacement slot) const { + if (!ptr_) + return {}; + return (*ptr_)[slot]; +} + +void Value::Comments::set(CommentPlacement slot, String comment) { + if (slot >= CommentPlacement::numberOfCommentPlacement) + return; + if (!ptr_) + ptr_ = std::unique_ptr(new Array()); + (*ptr_)[slot] = std::move(comment); +} + +void Value::setComment(String comment, CommentPlacement placement) { + if (!comment.empty() && (comment.back() == '\n')) { + // Always discard trailing newline, to aid indentation. + comment.pop_back(); + } + JSON_ASSERT(!comment.empty()); + JSON_ASSERT_MESSAGE( + comment[0] == '\0' || comment[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + comments_.set(placement, std::move(comment)); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_.has(placement); +} + +String Value::getComment(CommentPlacement placement) const { + return comments_.get(placement); +} + +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } + +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } + +ptrdiff_t Value::getOffsetStart() const { return start_; } + +ptrdiff_t Value::getOffsetLimit() const { return limit_; } + +String Value::toStyledString() const { + StreamWriterBuilder builder; + + String out = this->hasComment(commentBefore) ? "\n" : ""; + out += Json::writeString(builder, *this); + out += '\n'; + + return out; +} + +Value::const_iterator Value::begin() const { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return {}; +} + +Value::const_iterator Value::end() const { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return {}; +} + +Value::iterator Value::begin() { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type()) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() = default; + +PathArgument::PathArgument(ArrayIndex index) + : index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {} + +PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2, + const PathArgument& a3, const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.reserve(5); + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const String& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + auto itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *++current != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.' || *current == ']') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(String(beginName, current)); + } + } +} + +void Path::addPathInArg(const String& /*path*/, const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg++); + } +} + +void Path::invalidPath(const String& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... ) + return Value::nullSingleton(); + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + return Value::nullSingleton(); + } + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) { + // Error: unable to resolve path (object has no member named '' at + // position...) + return Value::nullSingleton(); + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (const auto& arg : args_) { + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __cplusplus >= 201103L +#include +#include + +#if !defined(isnan) +#define isnan std::isnan +#endif + +#if !defined(isfinite) +#define isfinite std::isfinite +#endif + +#else +#include +#include + +#if defined(_MSC_VER) +#if !defined(isnan) +#include +#define isnan _isnan +#endif + +#if !defined(isfinite) +#include +#define isfinite _finite +#endif + +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES + +#endif //_MSC_VER + +#if defined(__sun) && defined(__SVR4) // Solaris +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#endif + +#if defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) \ + ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x))) +#endif +#endif +#endif + +#if !defined(isnan) +// IEEE standard states that NaN values will not compare to themselves +#define isnan(x) ((x) != (x)) +#endif + +#if !defined(__APPLE__) +#if !defined(isfinite) +#define isfinite finite +#endif +#endif +#endif + +#if defined(_MSC_VER) +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +using StreamWriterPtr = std::unique_ptr; +#else +using StreamWriterPtr = std::auto_ptr; +#endif + +String valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} + +String valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +String valueToString(Int value) { return valueToString(LargestInt(value)); } + +String valueToString(UInt value) { return valueToString(LargestUInt(value)); } + +#endif // # if defined(JSON_HAS_INT64) + +namespace { +String valueToString(double value, bool useSpecialFloats, + unsigned int precision, PrecisionType precisionType) { + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distinguish the + // concepts of reals and integers. + if (!isfinite(value)) { + static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, + {"null", "-1e+9999", "1e+9999"}}; + return reps[useSpecialFloats ? 0 : 1] + [isnan(value) ? 0 : (value < 0) ? 1 : 2]; + } + + String buffer(size_t(36), '\0'); + while (true) { + int len = jsoncpp_snprintf( + &*buffer.begin(), buffer.size(), + (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f", + precision, value); + assert(len >= 0); + auto wouldPrint = static_cast(len); + if (wouldPrint >= buffer.size()) { + buffer.resize(wouldPrint + 1); + continue; + } + buffer.resize(wouldPrint); + break; + } + + buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end()); + + // try to ensure we preserve the fact that this was given to us as a double on + // input + if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) { + buffer += ".0"; + } + + // strip the zero padding from the right + if (precisionType == PrecisionType::decimalPlaces) { + buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision), + buffer.end()); + } + + return buffer; +} +} // namespace + +String valueToString(double value, unsigned int precision, + PrecisionType precisionType) { + return valueToString(value, false, precision, precisionType); +} + +String valueToString(bool value) { return value ? "true" : "false"; } + +static bool doesAnyCharRequireEscaping(char const* s, size_t n) { + assert(s || !n); + + return std::any_of(s, s + n, [](unsigned char c) { + return c == '\\' || c == '"' || c < 0x20 || c > 0x7F; + }); +} + +static unsigned int utf8ToCodepoint(const char*& s, const char* e) { + const unsigned int REPLACEMENT_CHARACTER = 0xFFFD; + + unsigned int firstByte = static_cast(*s); + + if (firstByte < 0x80) + return firstByte; + + if (firstByte < 0xE0) { + if (e - s < 2) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = + ((firstByte & 0x1F) << 6) | (static_cast(s[1]) & 0x3F); + s += 1; + // oversized encoded characters are invalid + return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF0) { + if (e - s < 3) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x0F) << 12) | + ((static_cast(s[1]) & 0x3F) << 6) | + (static_cast(s[2]) & 0x3F); + s += 2; + // surrogates aren't valid codepoints itself + // shouldn't be UTF-8 encoded + if (calculated >= 0xD800 && calculated <= 0xDFFF) + return REPLACEMENT_CHARACTER; + // oversized encoded characters are invalid + return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF8) { + if (e - s < 4) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x07) << 18) | + ((static_cast(s[1]) & 0x3F) << 12) | + ((static_cast(s[2]) & 0x3F) << 6) | + (static_cast(s[3]) & 0x3F); + s += 3; + // oversized encoded characters are invalid + return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; + } + + return REPLACEMENT_CHARACTER; +} + +static const char hex2[] = "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + +static String toHex16Bit(unsigned int x) { + const unsigned int hi = (x >> 8) & 0xff; + const unsigned int lo = x & 0xff; + String result(4, ' '); + result[0] = hex2[2 * hi]; + result[1] = hex2[2 * hi + 1]; + result[2] = hex2[2 * lo]; + result[3] = hex2[2 * lo + 1]; + return result; +} + +static void appendRaw(String& result, unsigned ch) { + result += static_cast(ch); +} + +static void appendHex(String& result, unsigned ch) { + result.append("\\u").append(toHex16Bit(ch)); +} + +static String valueToQuotedStringN(const char* value, size_t length, + bool emitUTF8 = false) { + if (value == nullptr) + return ""; + + if (!doesAnyCharRequireEscaping(value, length)) + return String("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to String is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL + String result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else { + appendRaw(result, codepoint); + } + } else { + unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c` + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else if (codepoint < 0x80) { + appendRaw(result, codepoint); + } else if (codepoint < 0x10000) { + // Basic Multilingual Plane + appendHex(result, codepoint); + } else { + // Extended Unicode. Encode 20 bits as a surrogate pair. + codepoint -= 0x10000; + appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff)); + appendHex(result, 0xdc00 + (codepoint & 0x3ff)); + } + } + } break; + } + } + result += "\""; + return result; +} + +String valueToQuotedString(const char* value) { + return valueToQuotedStringN(value, strlen(value)); +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() = default; + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + + = default; + +void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +String FastWriter::write(const Value& root) { + document_.clear(); + writeValue(root); + if (!omitEndingLineFeed_) + document_ += '\n'; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + document_ += valueToQuotedStringN(str, static_cast(end - str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (auto it = members.begin(); it != members.end(); ++it) { + const String& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), name.length()); + document_ += yamlCompatibilityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() = default; + +String StyledWriter::write(const Value& root) { + document_.clear(); + addChildValues_ = false; + indentString_.clear(); + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += '\n'; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + const String& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + size_t size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + ArrayIndex index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (size_t index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const String& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const String& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(indentString_.size() >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += '\n'; + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += '\n'; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += '\n'; + document_ += root.getComment(commentAfter); + document_ += '\n'; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(String indentation) + : document_(nullptr), indentation_(std::move(indentation)), + addChildValues_(), indented_(false) {} + +void StyledStreamWriter::write(OStream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_.clear(); + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = nullptr; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + const String& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultilineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const String& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const String& value) { + if (!indented_) + writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter { + BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs, + String colonSymbol, String nullSymbol, + String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, + PrecisionType precisionType); + int write(Value const& root, OStream* sout) override; + +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultilineArray(Value const& value); + void pushValue(String const& value); + void writeIndent(); + void writeWithIndent(String const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + using ChildValues = std::vector; + + ChildValues childValues_; + String indentString_; + unsigned int rightMargin_; + String indentation_; + CommentStyle::Enum cs_; + String colonSymbol_; + String nullSymbol_; + String endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + bool emitUTF8_ : 1; + unsigned int precision_; + PrecisionType precisionType_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + String indentation, CommentStyle::Enum cs, String colonSymbol, + String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, PrecisionType precisionType) + : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), + colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), + endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), + addChildValues_(false), indented_(false), + useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8), + precision_(precision), precisionType_(precisionType) {} +int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_.clear(); + writeCommentBeforeValue(root); + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = nullptr; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, + precisionType_)); + break; + case stringValue: { + // Is NULL is possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) + pushValue( + valueToQuotedStringN(str, static_cast(end - str), emitUTF8_)); + else + pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + auto it = members.begin(); + for (;;) { + String const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent( + valueToQuotedStringN(name.data(), name.length(), emitUTF8_)); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) + writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) + *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ((!indentation_.empty()) ? ", " : ","); + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) + *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + !childValue.empty()); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(String const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(String const& value) { + if (!indented_) + writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) + return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine( + Value const& root) { + if (cs_ == CommentStyle::None) + return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() : sout_(nullptr) {} +StreamWriter::~StreamWriter() = default; +StreamWriter::Factory::~Factory() = default; +StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } +StreamWriterBuilder::~StreamWriterBuilder() = default; +StreamWriter* StreamWriterBuilder::newStreamWriter() const { + const String indentation = settings_["indentation"].asString(); + const String cs_str = settings_["commentStyle"].asString(); + const String pt_str = settings_["precisionType"].asString(); + const bool eyc = settings_["enableYAMLCompatibility"].asBool(); + const bool dnp = settings_["dropNullPlaceholders"].asBool(); + const bool usf = settings_["useSpecialFloats"].asBool(); + const bool emitUTF8 = settings_["emitUTF8"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + PrecisionType precisionType(significantDigits); + if (pt_str == "significant") { + precisionType = PrecisionType::significantDigits; + } else if (pt_str == "decimal") { + precisionType = PrecisionType::decimalPlaces; + } else { + throwRuntimeError("precisionType must be 'significant' or 'decimal'"); + } + String colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + String nullSymbol = "null"; + if (dnp) { + nullSymbol.clear(); + } + if (pre > 17) + pre = 17; + String endingLineFeedSymbol; + return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, + endingLineFeedSymbol, usf, emitUTF8, pre, + precisionType); +} + +bool StreamWriterBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "indentation", + "commentStyle", + "enableYAMLCompatibility", + "dropNullPlaceholders", + "useSpecialFloats", + "emitUTF8", + "precision", + "precisionType", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[key] = *si; + else + return false; + } + return invalid ? invalid->empty() : true; +} + +Value& StreamWriterBuilder::operator[](const String& key) { + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) { + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["emitUTF8"] = false; + (*settings)["precision"] = 17; + (*settings)["precisionType"] = "significant"; + //! [StreamWriterBuilderDefaults] +} + +String writeString(StreamWriter::Factory const& factory, Value const& root) { + OStringStream sout; + StreamWriterPtr const writer(factory.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +OStream& operator<<(OStream& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + diff --git a/src/remote/src/remote_node.cpp b/src/remote/src/remote_node.cpp new file mode 100644 index 0000000..2d70fcf --- /dev/null +++ b/src/remote/src/remote_node.cpp @@ -0,0 +1,282 @@ +#include "MQTTClient.h" +#include "rclcpp/rclcpp.hpp" +#include "sweeper_interfaces/msg/mc_ctrl.hpp" +#include "json.h" +#include "remote_node.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sweeperMsg = sweeper_interfaces::msg; + +Json::Value root; +Json::Reader reader; +Json::Value data; +std::string command; +std::string remote_topic; +int time_cunt = 0; +// constexpr auto ADDRESS = "tcp://36.153.162.171:19583"; // 更改此处地址 +constexpr auto ADDRESS = "tcp://192.168.4.196:11883"; // 更改此处地址 +constexpr auto CLIENTID_REMOTE = "SWEEPER_CLIENT_REMOTE"; // 更改此处客户端ID + +const char *getRemoteTopic() +{ + return remote_topic.c_str(); +} + +constexpr auto QOS = 1; +constexpr auto TIMEOUT = 10000L; + +volatile MQTTClient_deliveryToken deliveredtoken; + +char remote_buff[500]; + +car_ctrl car_ctrl_mes; + +void delivered(void *context, MQTTClient_deliveryToken dt) +{ + (void)context; + printf("Message with token value %d delivery confirmed\n", dt); + deliveredtoken = dt; +} + +int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) +{ + (void)context; + (void)topicLen; + time_cunt = 0; + printf("Message arrived\n"); + printf("topic: %s\n", topicName); + printf("message: %.*s\n", message->payloadlen, (char *)message->payload); + + memset(remote_buff, 0, sizeof(remote_buff)); + memcpy(&remote_buff, (char *)message->payload, message->payloadlen); + + root.clear(); + + if (!reader.parse(remote_buff, root)) + { + printf("recv json fail\n"); + return 1; + } + else + { + std::cout << root << std::endl; + + data = root["data"]; + command = root["command"].asString(); + + if (command.compare("gear") == 0) + { + car_ctrl_mes.gear = root["value"].asInt(); + } + else if (command.compare("throttle") == 0) + { + car_ctrl_mes.throttle = root["value"].asFloat(); + } + else if (command.compare("steering") == 0) + { + car_ctrl_mes.steering = root["value"].asFloat(); + } + else if (command.compare("brake") == 0) + { + car_ctrl_mes.brake = root["value"].asFloat(); + } + else if (command.compare("sweep") == 0) + { + car_ctrl_mes.sweep = root["value"].asInt(); + } + } + + MQTTClient_freeMessage(&message); + MQTTClient_free(topicName); + return 1; +} + +void connlost(void *context, char *cause) +{ + (void)context; + printf("\nConnection lost\n"); + printf("cause: %s\n", cause); +} + +void *mqtt_remote(void *arg) +{ + (void)arg; + MQTTClient client; + // MQTTClient_deliveryToken token_d_m; + + MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; + // MQTTClient_message pubmsg_d_m = MQTTClient_message_initializer; + const char *REMOTE_TOPIC = getRemoteTopic(); + + int rc; + char *username = NULL; + char *password = NULL; + + if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID_REMOTE, MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to create client, return code %d\n", rc); + MQTTClient_destroy(&client); + return NULL; + } + + if ((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to set callbacks, return code %d\n", rc); + MQTTClient_destroy(&client); + return NULL; + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + conn_opts.username = username; + conn_opts.password = password; + + if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to connect, return code %d\n", rc); + MQTTClient_destroy(&client); + return NULL; + } + + printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n", REMOTE_TOPIC, CLIENTID_REMOTE, QOS); + if ((rc = MQTTClient_subscribe(client, REMOTE_TOPIC, QOS)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to subscribe, return code %d\n", rc); + MQTTClient_destroy(&client); + return NULL; + } + + while (1) + { + usleep(200000); + } + + if ((rc = MQTTClient_unsubscribe(client, REMOTE_TOPIC)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to unsubscribe, return code %d\n", rc); + MQTTClient_destroy(&client); + return NULL; + } + + if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to disconnect, return code %d\n", rc); + MQTTClient_destroy(&client); + return NULL; + } + + MQTTClient_destroy(&client); + return NULL; +} + +class remote_node : public rclcpp::Node +{ +public: + // 构造函数,有一个参数为节点名称 + remote_node(std::string name) : Node(name) + { + RCLCPP_INFO(this->get_logger(), "%s节点已经启动.", name.c_str()); + msg_publisher_ = this->create_publisher("ctrl_command", 10); + timer_ = this->create_wall_timer(std::chrono::milliseconds(100), std::bind(&remote_node::timer_callback, this)); + } + +private: + void timer_callback() + { + sweeperMsg::McCtrl message; + message.gear = car_ctrl_mes.gear; + if (car_ctrl_mes.gear == 0) // N档 + message.gear = 0; + else if (car_ctrl_mes.gear == 1) // D档 + message.gear = 2; + else if (car_ctrl_mes.gear == 2) // R档 + message.gear = 1; + + // 刹车 + message.brake = (car_ctrl_mes.brake > 0) ? true : false; + + // 油门 映射到 [0, 1000] 范围 + message.rpm = car_ctrl_mes.throttle / 65535.0f * 1000.0f; + + // 转向 + // 0~32200 -> -40~0 33200~65535 -> 0~40 + float steering = 0; + if (car_ctrl_mes.steering < 32200) + { + const double originalWidth1 = 32200.0; + const double targetWidth1 = 40.0; + + double unitLength1 = targetWidth1 / originalWidth1; + + steering = -40 + static_cast(car_ctrl_mes.steering * unitLength1); + } + else if (car_ctrl_mes.steering > 33200) + { + const double originalWidth2 = 32355.0; // 注意这里是从 33200 到 65535,共计 32355 个数 + const double targetWidth2 = 40.0; + + double unitLength2 = targetWidth2 / originalWidth2; + + steering = static_cast((car_ctrl_mes.steering - 32200) * unitLength2); + } + else + { + steering = 0.0; // 朝向前方 + } + message.angle = steering; + + message.sweep = car_ctrl_mes.sweep; + + msg_publisher_->publish(message); + } + + // 声名定时器指针 + rclcpp::TimerBase::SharedPtr timer_; + // 声明话题发布者指针 + rclcpp::Publisher::SharedPtr msg_publisher_; +}; + +void init_main() +{ + Json::Reader reader; + Json::Value root; + std::ifstream in("config.json", std::ios::binary); + if (!in.is_open()) + { + std::cout << "read config file error" << std::endl; + return; + } + if (reader.parse(in, root)) + { + remote_topic = root["mqtt"]["remote_topic"].asString(); + } +} + +pthread_t mqtt_remote_thread_t; + +int main(int argc, char **argv) +{ + init_main(); + memset(&car_ctrl_mes, 0, sizeof(car_ctrl_mes)); + pthread_create(&mqtt_remote_thread_t, NULL, mqtt_remote, NULL); + + rclcpp::init(argc, argv); + /*创建对应节点的共享指针对象*/ + auto node = std::make_shared("remote_node"); + /* 运行节点,并检测退出信号*/ + rclcpp::spin(node); + rclcpp::shutdown(); + + return 0; +} \ No newline at end of file