add paho.mqtt.c for mqtt client

This commit is contained in:
MysticBoy 2020-05-23 16:52:39 +08:00
parent aa9b3e3188
commit f8ffd8cbfc
63 changed files with 28046 additions and 0 deletions

1
deps/CMakeLists.txt vendored
View File

@ -7,3 +7,4 @@ ADD_SUBDIRECTORY(regex)
ADD_SUBDIRECTORY(iconv)
ADD_SUBDIRECTORY(lz4)
ADD_SUBDIRECTORY(cJson)
ADD_SUBDIRECTORY(paho.mqtt.c)

5
deps/paho.mqtt.c/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,5 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(TDengine)
INCLUDE_DIRECTORIES(inc)
AUX_SOURCE_DIRECTORY(src SRC)
ADD_LIBRARY(pahomqtt ${SRC})

83
deps/paho.mqtt.c/inc/Base64.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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 */

160
deps/paho.mqtt.c/inc/Clients.h vendored Normal file
View File

@ -0,0 +1,160 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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 "MQTTTime.h"
#if defined(OPENSSL)
#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
#endif
#include <openssl/ssl.h>
#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;
START_TIME_TYPE 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;
START_TIME_TYPE lastSent;
START_TIME_TYPE lastReceived;
START_TIME_TYPE lastPing;
#if defined(OPENSSL)
SSL* ssl;
SSL_CTX* ctx;
char *https_proxy;
char *https_proxy_auth;
#endif
char *http_proxy;
char *http_proxy_auth;
int websocket; /**< socket has been upgraded to use web sockets */
char *websocket_key;
const MQTTClient_nameValue* httpHeaders;
} 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
/** Proxy connection in progress */
#define PROXY_CONNECT_IN_PROGRESS 0x5
/** 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; /**< network info for this client */
int msgID; /**< the MQTT message id */
int keepAliveInterval; /**< the MQTT keep alive interval */
int retryInterval;
int maxInflightMessages; /**< the max number of inflight outbound messages we allow */
willMessages* will; /**< the MQTT will message, if any */
List* inboundMsgs; /**< inbound in flight messages */
List* outboundMsgs; /**< outbound in flight messages */
List* messageQueue; /**< inbound complete but undelivered messages */
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; /**< the version of MQTT being used, 3, 4 or 5 */
int sessionExpiry; /**< MQTT 5 session expiry */
#if defined(OPENSSL)
MQTTClient_SSLOptions *sslopts; /**< the SSL/TLS connect options */
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

90
deps/paho.mqtt.c/inc/Heap.h vendored Normal file
View File

@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp. and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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
#define PAHO_MEMORY_ERROR -99
#include "MQTTExportDeclarations.h"
#include <stdio.h>
#include <stdlib.h>
#if !defined(NO_HEAP_TRACKING)
#if !defined(TREE_C)
/**
* 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);
LIBMQTT_API 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
#endif

105
deps/paho.mqtt.c/inc/LinkedList.h vendored Normal file
View File

@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - updates for the async client
* Ian Craggs - change size types from int to size_t
*******************************************************************************/
#if !defined(LINKEDLIST_H)
#define LINKEDLIST_H
#include <stdlib.h> /* for size_t definition */
/*BE
defm defList(T)
def T concat Item
{
at 4
n32 ptr T concat Item suppress "next"
at 0
n32 ptr T concat Item suppress "prev"
at 8
n32 ptr T id2str(T)
}
def T concat List
{
n32 ptr T concat Item suppress "first"
n32 ptr T concat Item suppress "last"
n32 ptr T concat Item suppress "current"
n32 dec "count"
n32 suppress "size"
}
endm
defList(INT)
defList(STRING)
defList(TMP)
BE*/
/**
* Structure to hold all data for one list element
*/
typedef struct ListElementStruct
{
struct ListElementStruct *prev, /**< pointer to previous list element */
*next; /**< pointer to next list element */
void* content; /**< pointer to element content */
} ListElement;
/**
* Structure to hold all data for one list
*/
typedef struct
{
ListElement *first, /**< first element in the list */
*last, /**< last element in the list */
*current; /**< current element in the list, for iteration */
int count; /**< no of items */
size_t size; /**< heap storage used */
} List;
void ListZero(List*);
List* ListInitialize(void);
ListElement* ListAppend(List* aList, void* content, size_t size);
void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size);
ListElement* 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

85
deps/paho.mqtt.c/inc/Log.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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

2189
deps/paho.mqtt.c/inc/MQTTAsync.h vendored Normal file

File diff suppressed because it is too large Load Diff

1868
deps/paho.mqtt.c/inc/MQTTClient.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
/**
* @file
* \brief This structure represents a persistent data store, used to store
* outbound and inbound messages, in order to achieve reliable messaging.
*
* The MQTT Client persists QoS1 and QoS2 messages in order to meet the
* assurances of delivery associated with these @ref qos levels. The messages
* are saved in persistent storage
* The type and context of the persistence implementation are specified when
* the MQTT client is created (see MQTTClient_create()). The default
* persistence type (::MQTTCLIENT_PERSISTENCE_DEFAULT) uses a file system-based
* persistence mechanism. The <i>persistence_context</i> argument passed to
* MQTTClient_create() when using the default peristence is a string
* representing the location of the persistence directory. If the context
* argument is NULL, the working directory will be used.
*
* To use memory-based persistence, an application passes
* ::MQTTCLIENT_PERSISTENCE_NONE as the <i>persistence_type</i> to
* MQTTClient_create(). This can lead to message loss in certain situations,
* but can be appropriate in some cases (see @ref qos).
*
* Client applications can provide their own persistence mechanism by passing
* ::MQTTCLIENT_PERSISTENCE_USER as the <i>persistence_type</i>. To implement a
* custom persistence mechanism, the application must pass an initialized
* ::MQTTClient_persistence structure as the <i>persistence_context</i>
* argument to MQTTClient_create().
*
* If the functions defined return an ::MQTTCLIENT_PERSISTENCE_ERROR then the
* state of the persisted data should remain as it was prior to the function
* being called. For example, if Persistence_put() returns
* ::MQTTCLIENT_PERSISTENCE_ERROR, then it is assumed tha tthe persistent store
* does not contain the data that was passed to the function. Similarly, if
* Persistence_remove() returns ::MQTTCLIENT_PERSISTENCE_ERROR then it is
* assumed that the data to be removed is still held in the persistent store.
*
* It is up to the persistence implementation to log any error information that
* may be required to diagnose a persistence mechanism failure.
*/
/*
/// @cond EXCLUDE
*/
#if !defined(MQTTCLIENTPERSISTENCE_H)
#define MQTTCLIENTPERSISTENCE_H
/*
/// @endcond
*/
/**
* This <i>persistence_type</i> value specifies the default file system-based
* persistence mechanism (see MQTTClient_create()).
*/
#define MQTTCLIENT_PERSISTENCE_DEFAULT 0
/**
* This <i>persistence_type</i> value specifies a memory-based
* persistence mechanism (see MQTTClient_create()).
*/
#define MQTTCLIENT_PERSISTENCE_NONE 1
/**
* This <i>persistence_type</i> value specifies an application-specific
* persistence mechanism (see MQTTClient_create()).
*/
#define MQTTCLIENT_PERSISTENCE_USER 2
/**
* Application-specific persistence functions must return this error code if
* there is a problem executing the function.
*/
#define MQTTCLIENT_PERSISTENCE_ERROR -2
/**
* @brief Initialize the persistent store.
*
* Either open the existing persistent store for this client ID or create a new
* one if one doesn't exist. If the persistent store is already open, return
* without taking any action.
*
* An application can use the same client identifier to connect to many
* different servers. The <i>clientid</i> in conjunction with the
* <i>serverURI</i> uniquely identifies the persistence store required.
*
* @param handle The address of a pointer to a handle for this persistence
* implementation. This function must set handle to a valid reference to the
* persistence following a successful return.
* The handle pointer is passed as an argument to all the other
* persistence functions. It may include the context parameter and/or any other
* data for use by the persistence functions.
* @param clientID The client identifier for which the persistent store should
* be opened.
* @param serverURI The connection string specified when the MQTT client was
* created (see MQTTClient_create()).
* @param context A pointer to any data required to initialize the persistent
* store (see ::MQTTClient_persistence).
* @return Return 0 if the function completes successfully, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_open)(void** handle, const char* clientID, const char* serverURI, void* context);
/**
* @brief Close the persistent store referred to by the handle.
*
* @param handle The handle pointer from a successful call to
* Persistence_open().
* @return Return 0 if the function completes successfully, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_close)(void* handle);
/**
* @brief Put the specified data into the persistent store.
*
* @param handle The handle pointer from a successful call to
* Persistence_open().
* @param key A string used as the key for the data to be put in the store. The
* key is later used to retrieve data from the store with Persistence_get().
* @param bufcount The number of buffers to write to the persistence store.
* @param buffers An array of pointers to the data buffers associated with
* this <i>key</i>.
* @param buflens An array of lengths of the data buffers. <i>buflen[n]</i>
* gives the length of <i>buffer[n]</i>.
* @return Return 0 if the function completes successfully, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_put)(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
/**
* @brief Retrieve the specified data from the persistent store.
*
* @param handle The handle pointer from a successful call to
* Persistence_open().
* @param key A string that is the key for the data to be retrieved. This is
* the same key used to save the data to the store with Persistence_put().
* @param buffer The address of a pointer to a buffer. This function sets the
* pointer to point at the retrieved data, if successful.
* @param buflen The address of an int that is set to the length of
* <i>buffer</i> by this function if successful.
* @return Return 0 if the function completes successfully, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_get)(void* handle, char* key, char** buffer, int* buflen);
/**
* @brief Remove the data for the specified key from the store.
*
* @param handle The handle pointer from a successful call to
* Persistence_open().
* @param key A string that is the key for the data to be removed from the
* store. This is the same key used to save the data to the store with
* Persistence_put().
* @return Return 0 if the function completes successfully, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_remove)(void* handle, char* key);
/**
* @brief Returns the keys in this persistent data store.
*
* @param handle The handle pointer from a successful call to
* Persistence_open().
* @param keys The address of a pointer to pointers to strings. Assuming
* successful execution, this function allocates memory to hold the returned
* keys (strings used to store the data with Persistence_put()). It also
* allocates memory to hold an array of pointers to these strings. <i>keys</i>
* is set to point to the array of pointers to strings.
* @param nkeys A pointer to the number of keys in this persistent data store.
* This function sets the number of keys, if successful.
* @return Return 0 if the function completes successfully, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_keys)(void* handle, char*** keys, int* nkeys);
/**
* @brief Clears the persistence store, so that it no longer contains any
* persisted data.
*
* @param handle The handle pointer from a successful call to
* Persistence_open().
* @return Return 0 if the function completes successfully, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_clear)(void* handle);
/**
* @brief Returns whether any data has been persisted using the specified key.
*
* @param handle The handle pointer from a successful call to
* Persistence_open().
* @param key The string to be tested for existence in the store.
* @return Return 0 if the key was found in the store, otherwise return
* ::MQTTCLIENT_PERSISTENCE_ERROR.
*/
typedef int (*Persistence_containskey)(void* handle, char* key);
/**
* @brief A structure containing the function pointers to a persistence
* implementation and the context or state that will be shared across all
* the persistence functions.
*/
typedef struct {
/**
* A pointer to any data required to initialize the persistent store.
*/
void* context;
/**
* A function pointer to an implementation of Persistence_open().
*/
Persistence_open popen;
/**
* A function pointer to an implementation of Persistence_close().
*/
Persistence_close pclose;
/**
* A function pointer to an implementation of Persistence_put().
*/
Persistence_put pput;
/**
* A function pointer to an implementation of Persistence_get().
*/
Persistence_get pget;
/**
* A function pointer to an implementation of Persistence_remove().
*/
Persistence_remove premove;
/**
* A function pointer to an implementation of Persistence_keys().
*/
Persistence_keys pkeys;
/**
* A function pointer to an implementation of Persistence_clear().
*/
Persistence_clear pclear;
/**
* A function pointer to an implementation of Persistence_containskey().
*/
Persistence_containskey pcontainskey;
} MQTTClient_persistence;
#endif

View File

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2020, 2020 Andreas Walter
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Andreas Walter - initially moved export declarations into separate fle
*******************************************************************************/
#if !defined(EXPORTDECLARATIONS_H)
#define EXPORTDECLARATIONS_H
#if defined(_WIN32) || defined(_WIN64)
# if defined(PAHO_MQTT_EXPORTS)
# define LIBMQTT_API __declspec(dllexport)
# elif defined(PAHO_MQTT_IMPORTS)
# define LIBMQTT_API __declspec(dllimport)
# else
# define LIBMQTT_API
# endif
#else
# if defined(PAHO_MQTT_EXPORTS)
# define LIBMQTT_API __attribute__ ((visibility ("default")))
# else
# define LIBMQTT_API extern
# endif
#endif
#endif

270
deps/paho.mqtt.c/inc/MQTTPacket.h vendored Normal file
View File

@ -0,0 +1,270 @@
/*******************************************************************************
* Copyright (c) 2009, 2019 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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 <endian.h>
#if __BYTE_ORDER == __BIG_ENDIAN
#define REVERSED 1
#endif
#endif
/**
* Bitfields for the MQTT header byte.
*/
typedef union
{
/*unsigned*/ char byte; /**< the whole byte */
#if defined(REVERSED)
struct
{
unsigned int type : 4; /**< message type nibble */
bool dup : 1; /**< DUP flag bit */
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
bool retain : 1; /**< retained flag bit */
} bits;
#else
struct
{
bool retain : 1; /**< retained flag bit */
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
bool dup : 1; /**< DUP flag bit */
unsigned int type : 4; /**< message type nibble */
} bits;
#endif
} Header;
/**
* Data for a connect packet.
*/
typedef struct
{
Header header; /**< MQTT header byte */
union
{
unsigned char all; /**< all connect flags */
#if defined(REVERSED)
struct
{
bool username : 1; /**< 3.1 user name */
bool password : 1; /**< 3.1 password */
bool willRetain : 1; /**< will retain setting */
unsigned int willQoS : 2; /**< will QoS value */
bool will : 1; /**< will flag */
bool cleanstart : 1; /**< cleansession flag */
int : 1; /**< unused */
} bits;
#else
struct
{
int : 1; /**< unused */
bool cleanstart : 1; /**< cleansession flag */
bool will : 1; /**< will flag */
unsigned int willQoS : 2; /**< will QoS value */
bool willRetain : 1; /**< will retain setting */
bool password : 1; /**< 3.1 password */
bool username : 1; /**< 3.1 user name */
} bits;
#endif
} flags; /**< connect flags byte */
char *Protocol, /**< MQTT protocol name */
*clientID, /**< string client id */
*willTopic, /**< will topic */
*willMsg; /**< will payload */
int keepAliveTimer; /**< keepalive timeout value in seconds */
unsigned char version; /**< MQTT version number */
} Connect;
/**
* Data for a connack packet.
*/
typedef struct
{
Header header; /**< MQTT header byte */
union
{
unsigned char all; /**< all connack flags */
#if defined(REVERSED)
struct
{
unsigned int reserved : 7; /**< message type nibble */
bool sessionPresent : 1; /**< was a session found on the server? */
} bits;
#else
struct
{
bool sessionPresent : 1; /**< was a session found on the server? */
unsigned int reserved : 7; /**< message type nibble */
} bits;
#endif
} flags; /**< connack flags byte */
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 MQTTVersion, 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 MQTTVersion, int msgid, networkHandles* net, const char* clientID);
int MQTTPacket_send_pubrel(int MQTTVersion, int msgid, int dup, networkHandles* net, const char* clientID);
int MQTTPacket_send_pubcomp(int MQTTVersion, 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, unsigned int* value);
#include "MQTTPacketOut.h"
#endif /* MQTTPACKET_H */

39
deps/paho.mqtt.c/inc/MQTTPacketOut.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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

94
deps/paho.mqtt.c/inc/MQTTPersistence.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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 "c5-"
/** 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

View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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

219
deps/paho.mqtt.c/inc/MQTTProperties.h vendored Normal file
View File

@ -0,0 +1,219 @@
/*******************************************************************************
* Copyright (c) 2017, 2020 IBM Corp. and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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
#include "MQTTExportDeclarations.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 */
};
/**
* 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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 {
unsigned char byte; /**< holds the value of a byte property type */
unsigned short integer2; /**< holds the value of a 2 byte integer property type */
unsigned 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API 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.
*/
LIBMQTT_API MQTTProperty* MQTTProperties_getPropertyAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index);
#endif /* MQTTPROPERTIES_H */

46
deps/paho.mqtt.c/inc/MQTTProtocol.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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, int allocatePayload);
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 allocatePayload);
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(START_TIME_TYPE);
void MQTTProtocol_retry(START_TIME_TYPE, 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

60
deps/paho.mqtt.c/inc/MQTTProtocolOut.h vendored Normal file
View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2009, 2019 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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)
#if defined(__GNUC__) && defined(__linux__)
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int ssl, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout);
#else
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int ssl, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties);
#endif
#else
#if defined(__GNUC__) && defined(__linux__)
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout);
#else
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties);
#endif
#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

79
deps/paho.mqtt.c/inc/MQTTReasonCodes.h vendored Normal file
View File

@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (c) 2017, 2020 IBM Corp. and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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
#include "MQTTExportDeclarations.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
};
/**
* 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.
*/
LIBMQTT_API const char* MQTTReasonCode_toString(enum MQTTReasonCodes value);
#endif

View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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

39
deps/paho.mqtt.c/inc/MQTTTime.h vendored Normal file
View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial implementation
*******************************************************************************/
#if !defined(MQTTTIME_H)
#define MQTTTIME_H
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#define START_TIME_TYPE DWORD
#define START_TIME_ZERO 0
#elif defined(AIX)
#define START_TIME_TYPE struct timespec
#define START_TIME_ZERO {0, 0}
#else
#include <sys/time.h>
#define START_TIME_TYPE struct timeval
#define START_TIME_ZERO {0, 0}
#endif
void MQTTTime_sleep(long milliseconds);
START_TIME_TYPE MQTTTime_start_clock(void);
START_TIME_TYPE MQTTTime_now(void);
long MQTTTime_elapsed(START_TIME_TYPE milliseconds);
long MQTTTime_difftime(START_TIME_TYPE new, START_TIME_TYPE old);
#endif

24
deps/paho.mqtt.c/inc/Messages.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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

42
deps/paho.mqtt.c/inc/OsWrapper.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Gunter Raidl - timer support for VxWorks
* Rainer Poisel - reusability
*******************************************************************************/
#if !defined(OSWRAPPER_H)
#define OSWRAPPER_H
#if defined(_WRS_KERNEL)
#include <time.h>
#define lstat stat
typedef unsigned long useconds_t;
void usleep(useconds_t useconds);
#define timersub(a, b, result) \
do \
{ \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) \
{ \
--(result)->tv_sec; \
(result)->tv_usec += 1000000L; \
} \
} while (0)
#endif /* defined(_WRS_KERNEL) */
#endif /* OSWRAPPER_H */

91
deps/paho.mqtt.c/inc/SHA1.h vendored Normal file
View File

@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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 <openssl/sha.h>
/** SHA-1 Digest Length */
#define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
#else /* if defined(OPENSSL) */
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <wincrypt.h>
typedef struct SHA_CTX_S
{
HCRYPTPROV hProv;
HCRYPTHASH hHash;
} SHA_CTX;
#else /* if defined(_WIN32) || defined(_WIN64) */
#include <stdint.h>
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 <stddef.h>
/** 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 */

52
deps/paho.mqtt.c/inc/SSLSocket.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs, Allan Stockdill-Mander - initial implementation
* Ian Craggs - SNI support
* Ian Craggs - post connect checks and CApath
*******************************************************************************/
#if !defined(SSLSOCKET_H)
#define SSLSOCKET_H
#if defined(_WIN32) || defined(_WIN64)
#define ssl_mutex_type HANDLE
#else
#include <pthread.h>
#include <semaphore.h>
#define ssl_mutex_type pthread_mutex_t
#endif
#include <openssl/ssl.h>
#include "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

151
deps/paho.mqtt.c/inc/Socket.h vendored Normal file
View File

@ -0,0 +1,151 @@
/*******************************************************************************
* Copyright (c) 2009, 2019 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial implementation and documentation
* Ian Craggs - async client updates
*******************************************************************************/
#if !defined(SOCKET_H)
#define SOCKET_H
#include <sys/types.h>
#if defined(_WIN32) || defined(_WIN64)
#include <errno.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define MAXHOSTNAMELEN 256
#if !defined(SSLSOCKET_H)
#undef EAGAIN
#define EAGAIN WSAEWOULDBLOCK
#undef EINTR
#define EINTR WSAEINTR
#undef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#undef ENOTCONN
#define ENOTCONN WSAENOTCONN
#undef ECONNRESET
#define ECONNRESET WSAECONNRESET
#undef ETIMEDOUT
#define ETIMEDOUT WAIT_TIMEOUT
#endif
#define ioctl ioctlsocket
#define socklen_t int
#else
#define INVALID_SOCKET SOCKET_ERROR
#include <sys/socket.h>
#if !defined(_WRS_KERNEL)
#include <sys/param.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/uio.h>
#else
#include <selectLib.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#define ULONG size_t
#endif
#include "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);
#if defined(__GNUC__) && defined(__linux__)
/* able to use GNU's getaddrinfo_a to make timeouts possible */
int Socket_new(const char* addr, size_t addr_len, int port, int* socket, long timeout);
#else
int Socket_new(const char* addr, size_t addr_len, int port, int* socket);
#endif
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 */

84
deps/paho.mqtt.c/inc/SocketBuffer.h vendored Normal file
View File

@ -0,0 +1,84 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
#if !defined(SOCKETBUFFER_H)
#define SOCKETBUFFER_H
#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
#else
#include <sys/socket.h>
#endif
#if defined(OPENSSL)
#include <openssl/ssl.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
typedef WSABUF iobuf;
#else
typedef struct iovec iobuf;
#endif
typedef struct
{
int socket;
unsigned int index;
size_t headerlen;
char fixed_header[5]; /**< header plus up to 4 length bytes */
size_t buflen, /**< total length of the buffer */
datalen; /**< current length of data in buf */
char* buf;
} socket_queue;
typedef struct
{
int socket, count;
size_t total;
#if defined(OPENSSL)
SSL* ssl;
#endif
size_t bytes;
iobuf iovecs[5];
int frees[5];
} pending_writes;
#define SOCKETBUFFER_COMPLETE 0
#if !defined(SOCKET_ERROR)
#define SOCKET_ERROR -1
#endif
#define SOCKETBUFFER_INTERRUPTED -22 /* must be the same value as TCPSOCKET_INTERRUPTED */
int 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)
int SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes);
#else
int 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

75
deps/paho.mqtt.c/inc/StackTrace.h vendored Normal file
View File

@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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_
#if defined(HIGH_PERFORMANCE)
#define NOSTACKTRACE 1
#endif
#include <stdio.h>
#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__, NULL, -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_ */

78
deps/paho.mqtt.c/inc/Thread.h vendored Normal file
View File

@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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
*******************************************************************************/
#if !defined(THREAD_H)
#define THREAD_H
#include "MQTTExportDeclarations.h"
#include "MQTTClient.h"
#include "mutex_type.h" /* Needed for mutex_type */
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#define thread_type HANDLE
#define thread_id_type DWORD
#define thread_return_type DWORD
#define thread_fn LPTHREAD_START_ROUTINE
#define cond_type HANDLE
#define sem_type HANDLE
#undef ETIMEDOUT
#define ETIMEDOUT WSAETIMEDOUT
#else
#include <pthread.h>
#define thread_type pthread_t
#define thread_id_type pthread_t
#define thread_return_type void*
typedef thread_return_type (*thread_fn)(void*);
typedef struct { pthread_cond_t cond; pthread_mutex_t mutex; } cond_type_struct;
typedef cond_type_struct *cond_type;
#if defined(OSX)
#include <dispatch/dispatch.h>
typedef dispatch_semaphore_t sem_type;
#else
#include <semaphore.h>
typedef sem_t *sem_type;
#endif
cond_type Thread_create_cond(int*);
int Thread_signal_cond(cond_type);
int Thread_wait_cond(cond_type condvar, int timeout);
int Thread_destroy_cond(cond_type);
#endif
LIBMQTT_API thread_type Thread_start(thread_fn, void*);
LIBMQTT_API mutex_type Thread_create_mutex(int*);
LIBMQTT_API int Thread_lock_mutex(mutex_type);
LIBMQTT_API int Thread_unlock_mutex(mutex_type);
int Thread_destroy_mutex(mutex_type);
LIBMQTT_API thread_id_type Thread_getid();
sem_type Thread_create_sem(int*);
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

115
deps/paho.mqtt.c/inc/Tree.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial implementation and documentation
*******************************************************************************/
#if !defined(TREE_H)
#define TREE_H
#include <stdlib.h> /* for size_t definition */
/*BE
defm defTree(T) // macro to define a tree
def T concat Node
{
n32 ptr T concat Node "parent"
n32 ptr T concat Node "left"
n32 ptr T concat Node "right"
n32 ptr T id2str(T)
n32 suppress "size"
}
def T concat Tree
{
struct
{
n32 ptr T concat Node suppress "root"
n32 ptr DATA suppress "compare"
}
struct
{
n32 ptr T concat Node suppress "root"
n32 ptr DATA suppress "compare"
}
n32 dec "count"
n32 dec suppress "size"
}
endm
defTree(INT)
defTree(STRING)
defTree(TMP)
BE*/
/**
* Structure to hold all data for one list element
*/
typedef struct NodeStruct
{
struct NodeStruct *parent, /**< pointer to parent tree node, in case we need it */
*child[2]; /**< pointers to child tree nodes 0 = left, 1 = right */
void* content; /**< pointer to element content */
size_t size; /**< size of content */
unsigned int red : 1;
} Node;
/**
* Structure to hold all data for one tree
*/
typedef struct
{
struct
{
Node *root; /**< root node pointer */
int (*compare)(void*, void*, int); /**< comparison function */
} index[2];
int indexes, /**< no of indexes into tree */
count; /**< no of items */
size_t size; /**< heap storage used */
unsigned int heap_tracking : 1; /**< switch on heap tracking for this tree? */
unsigned int allow_duplicates : 1; /**< switch to allow duplicate entries */
} Tree;
Tree* TreeInitialize(int(*compare)(void*, void*, int));
void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int));
void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int));
void* TreeAdd(Tree* aTree, void* content, size_t size);
void* TreeRemove(Tree* aTree, void* content);
void* TreeRemoveKey(Tree* aTree, void* key);
void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index);
void* TreeRemoveNodeIndex(Tree* aTree, Node* aNode, int index);
void TreeFree(Tree* aTree);
Node* TreeFind(Tree* aTree, void* key);
Node* TreeFindIndex(Tree* aTree, void* key, int index);
Node* TreeNextElement(Tree* aTree, Node* curnode);
int TreeIntCompare(void* a, void* b, int);
int TreePtrCompare(void* a, void* b, int);
int TreeStringCompare(void* a, void* b, int);
#endif

7
deps/paho.mqtt.c/inc/VersionInfo.h vendored Normal file
View File

@ -0,0 +1,7 @@
#ifndef VERSIONINFO_H
#define VERSIONINFO_H
#define BUILD_TIMESTAMP "2020-05-23T04:16:51Z"
#define CLIENT_VERSION "1.3.2"
#endif /* VERSIONINFO_H */

76
deps/paho.mqtt.c/inc/WebSocket.h vendored Normal file
View File

@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (c) 2018, 2020 Wind River Systems, Inc. and others. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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);
/* 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);
/* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint */
int WebSocket_proxy_connect( networkHandles *net, int ssl, const char *hostname);
#endif /* WEBSOCKET_H */

25
deps/paho.mqtt.c/inc/mutex_type.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
*******************************************************************************/
#if !defined(_MUTEX_TYPE_H_)
#define _MUTEX_TYPE_H_
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#define mutex_type HANDLE
#else
#include <pthread.h>
#define mutex_type pthread_mutex_t*
#endif
#endif /* _MUTEX_TYPE_H_ */

23
deps/paho.mqtt.c/inc/utf-8.h vendored Normal file
View File

@ -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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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

352
deps/paho.mqtt.c/src/Base64.c vendored Normal file
View File

@ -0,0 +1,352 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Keith Holman - initial implementation and documentation
*******************************************************************************/
#include "Base64.h"
#if defined(_WIN32) || defined(_WIN64)
#pragma comment(lib, "crypt32.lib")
#include <windows.h>
#include <wincrypt.h>
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
{
b64_size_t ret = 0u;
DWORD dw_out_len = (DWORD)out_len;
if ( CryptStringToBinaryA( in, in_len, CRYPT_STRING_BASE64, out, &dw_out_len, NULL, NULL ) )
ret = (b64_size_t)dw_out_len;
return ret;
}
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
{
b64_size_t ret = 0u;
DWORD dw_out_len = (DWORD)out_len;
if ( CryptBinaryToStringA( in, in_len, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, out, &dw_out_len ) )
ret = (b64_size_t)dw_out_len;
return ret;
}
#else /* if defined(_WIN32) || defined(_WIN64) */
#if defined(OPENSSL)
#include <openssl/bio.h>
#include <openssl/evp.h>
static b64_size_t Base64_encodeDecode(
char *out, b64_size_t out_len, const char *in, b64_size_t in_len, int encode )
{
b64_size_t ret = 0u;
if ( in_len > 0u )
{
int rv;
BIO *bio, *b64, *b_in, *b_out;
b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bio);
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); /* ignore new-lines */
if ( encode )
{
b_in = bio;
b_out = b64;
}
else
{
b_in = b64;
b_out = bio;
}
rv = BIO_write(b_out, in, (int)in_len);
BIO_flush(b_out); /* indicate end of encoding */
if ( rv > 0 )
{
rv = BIO_read(b_in, out, (int)out_len);
if ( rv > 0 )
{
ret = (b64_size_t)rv;
if ( out_len > ret )
out[ret] = '\0';
}
}
BIO_free_all(b64); /* free all used memory */
}
return ret;
}
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
{
return Base64_encodeDecode( (char*)out, out_len, in, in_len, 0 );
}
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
{
return Base64_encodeDecode( out, out_len, (const char*)in, in_len, 1 );
}
#else /* if defined(OPENSSL) */
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
{
#define NV 64
static const unsigned char BASE64_DECODE_TABLE[] =
{
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 0-15 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 16-31 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, 62, NV, NV, NV, 63, /* 32-47 */
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, NV, NV, NV, NV, NV, NV, /* 48-63 */
NV, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 64-79 */
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, NV, NV, NV, NV, NV, /* 80-95 */
NV, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 96-111 */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, NV, NV, NV, NV, NV, /* 112-127 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 128-143 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 144-159 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 160-175 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 176-191 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 192-207 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 208-223 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 224-239 */
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV /* 240-255 */
};
b64_size_t ret = 0u;
b64_size_t out_count = 0u;
/* in valid base64, length must be multiple of 4's: 0, 4, 8, 12, etc */
while ( in_len > 3u && out_count < out_len )
{
int i;
unsigned char c[4];
for ( i = 0; i < 4; ++i, ++in )
c[i] = BASE64_DECODE_TABLE[(int)(*in)];
in_len -= 4u;
/* first byte */
*out = c[0] << 2;
*out |= (c[1] & ~0xF) >> 4;
++out;
++out_count;
if ( out_count < out_len )
{
/* second byte */
*out = (c[1] & 0xF) << 4;
if ( c[2] < NV )
{
*out |= (c[2] & ~0x3) >> 2;
++out;
++out_count;
if ( out_count < out_len )
{
/* third byte */
*out = (c[2] & 0x3) << 6;
if ( c[3] < NV )
{
*out |= c[3];
++out;
++out_count;
}
else
in_len = 0u;
}
}
else
in_len = 0u;
}
}
if ( out_count <= out_len )
{
ret = out_count;
if ( out_count < out_len )
*out = '\0';
}
return ret;
}
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
{
static const char BASE64_ENCODE_TABLE[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/=";
b64_size_t ret = 0u;
b64_size_t out_count = 0u;
while ( in_len > 0u && out_count < out_len )
{
int i;
unsigned char c[] = { 0, 0, 64, 64 }; /* index of '=' char */
/* first character */
i = *in;
c[0] = (i & ~0x3) >> 2;
/* second character */
c[1] = (i & 0x3) << 4;
--in_len;
if ( in_len > 0u )
{
++in;
i = *in;
c[1] |= (i & ~0xF) >> 4;
/* third character */
c[2] = (i & 0xF) << 2;
--in_len;
if ( in_len > 0u )
{
++in;
i = *in;
c[2] |= (i & ~0x3F) >> 6;
/* fourth character */
c[3] = (i & 0x3F);
--in_len;
++in;
}
}
/* encode the characters */
out_count += 4u;
for ( i = 0; i < 4 && out_count <= out_len; ++i, ++out )
*out = BASE64_ENCODE_TABLE[c[i]];
}
if ( out_count <= out_len )
{
if ( out_count < out_len )
*out = '\0';
ret = out_count;
}
return ret;
}
#endif /* else if defined(OPENSSL) */
#endif /* if else defined(_WIN32) || defined(_WIN64) */
b64_size_t Base64_decodeLength( const char *in, b64_size_t in_len )
{
b64_size_t pad = 0u;
if ( in && in_len > 1u )
pad += ( in[in_len - 2u] == '=' ? 1u : 0u );
if ( in && in_len > 0u )
pad += ( in[in_len - 1u] == '=' ? 1u : 0u );
return (in_len / 4u * 3u) - pad;
}
b64_size_t Base64_encodeLength( const b64_data_t *in, b64_size_t in_len )
{
return ((4u * in_len / 3u) + 3u) & ~0x3;
}
#if defined(BASE64_TEST)
#include <stdio.h>
#include <string.h>
#define TEST_EXPECT(i,x) if (!(x)) {fprintf( stderr, "failed test: %s (for i == %d)\n", #x, i ); ++fails;}
int main(int argc, char *argv[])
{
struct _td
{
const char *in;
const char *out;
};
int i;
unsigned int fails = 0u;
struct _td test_data[] = {
{ "", "" },
{ "p", "cA==" },
{ "pa", "cGE=" },
{ "pah", "cGFo" },
{ "paho", "cGFobw==" },
{ "paho ", "cGFobyA=" },
{ "paho w", "cGFobyB3" },
{ "paho wi", "cGFobyB3aQ==" },
{ "paho wit", "cGFobyB3aXQ=" },
{ "paho with", "cGFobyB3aXRo" },
{ "paho with ", "cGFobyB3aXRoIA==" },
{ "paho with w", "cGFobyB3aXRoIHc=" },
{ "paho with we", "cGFobyB3aXRoIHdl" },
{ "paho with web", "cGFobyB3aXRoIHdlYg==" },
{ "paho with webs", "cGFobyB3aXRoIHdlYnM=" },
{ "paho with webso", "cGFobyB3aXRoIHdlYnNv" },
{ "paho with websoc", "cGFobyB3aXRoIHdlYnNvYw==" },
{ "paho with websock", "cGFobyB3aXRoIHdlYnNvY2s=" },
{ "paho with websocke", "cGFobyB3aXRoIHdlYnNvY2tl" },
{ "paho with websocket", "cGFobyB3aXRoIHdlYnNvY2tldA==" },
{ "paho with websockets", "cGFobyB3aXRoIHdlYnNvY2tldHM=" },
{ "paho with websockets.", "cGFobyB3aXRoIHdlYnNvY2tldHMu" },
{ "The quick brown fox jumps over the lazy dog",
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==" },
{ "Man is distinguished, not only by his reason, but by this singular passion from\n"
"other animals, which is a lust of the mind, that by a perseverance of delight\n"
"in the continued and indefatigable generation of knowledge, exceeds the short\n"
"vehemence of any carnal pleasure.",
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu"
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
"ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" },
{ NULL, NULL }
};
/* decode tests */
i = 0;
while ( test_data[i].in != NULL )
{
int r;
char out[512u];
r = Base64_decode( out, sizeof(out), test_data[i].out, strlen(test_data[i].out) );
TEST_EXPECT( i, r == strlen(test_data[i].in) && strncmp(out, test_data[i].in, strlen(test_data[i].in)) == 0 );
++i;
}
/* decode length tests */
i = 0;
while ( test_data[i].in != NULL )
{
TEST_EXPECT( i, Base64_decodeLength(test_data[i].out, strlen(test_data[i].out)) == strlen(test_data[i].in));
++i;
}
/* encode tests */
i = 0;
while ( test_data[i].in != NULL )
{
int r;
char out[512u];
r = Base64_encode( out, sizeof(out), test_data[i].in, strlen(test_data[i].in) );
TEST_EXPECT( i, r == strlen(test_data[i].out) && strncmp(out, test_data[i].out, strlen(test_data[i].out)) == 0 );
++i;
}
/* encode length tests */
i = 0;
while ( test_data[i].in != NULL )
{
TEST_EXPECT( i, Base64_encodeLength(test_data[i].in, strlen(test_data[i].in)) == strlen(test_data[i].out) );
++i;
}
if ( fails )
printf( "%u test failed!\n", fails );
else
printf( "all tests passed\n" );
return fails;
}
#endif /* if defined(BASE64_TEST) */

55
deps/paho.mqtt.c/src/Clients.c vendored Normal file
View File

@ -0,0 +1,55 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - add SSL support
*******************************************************************************/
/**
* @file
* \brief functions which apply to client structures
* */
#include "Clients.h"
#include <string.h>
#include <stdio.h>
/**
* List callback function for comparing clients by clientid
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int clientIDCompare(void* a, void* b)
{
Clients* client = (Clients*)a;
/*printf("comparing clientdIDs %s with %s\n", client->clientID, (char*)b);*/
return strcmp(client->clientID, (char*)b) == 0;
}
/**
* List callback function for comparing clients by socket
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int clientSocketCompare(void* a, void* b)
{
Clients* client = (Clients*)a;
/*printf("comparing %d with %d\n", (char*)a, (char*)b); */
return client->net.socket == *(int*)b;
}

528
deps/paho.mqtt.c/src/Heap.c vendored Normal file
View File

@ -0,0 +1,528 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - use tree data structure instead of list
* Ian Craggs - change roundup to Heap_roundup to avoid macro name clash on MacOSX
*******************************************************************************/
/**
* @file
* \brief functions to manage the heap with the goal of eliminating memory leaks
*
* For any module to use these functions transparently, simply include the Heap.h
* header file. Malloc and free will be redefined, but will behave in exactly the same
* way as normal, so no recoding is necessary.
*
* */
#include "Tree.h"
#include "Log.h"
#include "StackTrace.h"
#include "Thread.h"
#if defined(HEAP_UNIT_TESTS)
char* Broker_recordFFDC(char* symptoms);
#endif /* HEAP_UNIT_TESTS */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include "Heap.h"
#if !defined(NO_HEAP_TRACKING)
#undef malloc
#undef realloc
#undef free
#if defined(_WIN32) || defined(_WIN64)
mutex_type heap_mutex;
#else
static pthread_mutex_t heap_mutex_store = PTHREAD_MUTEX_INITIALIZER;
static mutex_type heap_mutex = &heap_mutex_store;
#endif
static heap_info state = {0, 0}; /**< global heap state information */
typedef double eyecatcherType;
static eyecatcherType eyecatcher = (eyecatcherType)0x8888888888888888;
/*#define HEAP_STACK 1 */
/**
* Each item on the heap is recorded with this structure.
*/
typedef struct
{
char* file; /**< the name of the source file where the storage was allocated */
int line; /**< the line no in the source file where it was allocated */
void* ptr; /**< pointer to the allocated storage */
size_t size; /**< size of the allocated storage */
#if defined(HEAP_STACK)
char* stack;
#endif
} storageElement;
static Tree heap; /**< Tree that holds the allocation records */
static const char *errmsg = "Memory allocation error";
static size_t Heap_roundup(size_t size);
static int ptrCompare(void* a, void* b, int value);
/*static void Heap_check(char* string, void* ptr);*/
static void checkEyecatchers(char* file, int line, void* p, size_t size);
static int Internal_heap_unlink(char* file, int line, void* p);
static void HeapScan(enum LOG_LEVELS log_level);
/**
* Round allocation size up to a multiple of the size of an int. Apart from possibly reducing fragmentation,
* on the old v3 gcc compilers I was hitting some weird behaviour, which might have been errors in
* sizeof() used on structures and related to packing. In any case, this fixes that too.
* @param size the size actually needed
* @return the rounded up size
*/
static size_t Heap_roundup(size_t size)
{
static int multsize = 4*sizeof(int);
if (size % multsize != 0)
size += multsize - (size % multsize);
return size;
}
/**
* List callback function for comparing storage elements
* @param a pointer to the current content in the tree (storageElement*)
* @param b pointer to the memory to free
* @return boolean indicating whether a and b are equal
*/
static int ptrCompare(void* a, void* b, int value)
{
a = ((storageElement*)a)->ptr;
if (value)
b = ((storageElement*)b)->ptr;
return (a > b) ? -1 : (a == b) ? 0 : 1;
}
/*
static void Heap_check(char* string, void* ptr)
{
Node* curnode = NULL;
storageElement* prev, *s = NULL;
printf("Heap_check start %p\n", ptr);
while ((curnode = TreeNextElement(&heap, curnode)) != NULL)
{
prev = s;
s = (storageElement*)(curnode->content);
if (prev)
{
if (ptrCompare(s, prev, 1) != -1)
{
printf("%s: heap order error %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
exit(99);
}
else
printf("%s: heap order good %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
}
}
}*/
/**
* Allocates a block of memory. A direct replacement for malloc, but keeps track of items
* allocated in a list, so that free can check that a item is being freed correctly and that
* we can check that all memory is freed at shutdown.
* @param file use the __FILE__ macro to indicate which file this item was allocated in
* @param line use the __LINE__ macro to indicate which line this item was allocated at
* @param size the size of the item to be allocated
* @return pointer to the allocated item, or NULL if there was an error
*/
void* mymalloc(char* file, int line, size_t size)
{
storageElement* s = NULL;
size_t space = sizeof(storageElement);
size_t filenamelen = strlen(file)+1;
void* rc = NULL;
Thread_lock_mutex(heap_mutex);
size = Heap_roundup(size);
if ((s = malloc(sizeof(storageElement))) == NULL)
{
Log(LOG_ERROR, 13, errmsg);
goto exit;
}
memset(s, 0, sizeof(storageElement));
s->size = size; /* size without eyecatchers */
if ((s->file = malloc(filenamelen)) == NULL)
{
Log(LOG_ERROR, 13, errmsg);
free(s);
goto exit;
}
memset(s->file, 0, sizeof(filenamelen));
space += filenamelen;
strcpy(s->file, file);
#if defined(HEAP_STACK)
#define STACK_LEN 300
if ((s->stack = malloc(STACK_LEN)) == NULL)
{
Log(LOG_ERROR, 13, errmsg);
free(s->file);
free(s);
goto exit;
}
memset(s->stack, 0, sizeof(filenamelen));
StackTrace_get(Thread_getid(), s->stack, STACK_LEN);
#endif
s->line = line;
/* Add space for eyecatcher at each end */
if ((s->ptr = malloc(size + 2*sizeof(eyecatcherType))) == NULL)
{
Log(LOG_ERROR, 13, errmsg);
free(s->file);
free(s);
goto exit;
}
memset(s->ptr, 0, size + 2*sizeof(eyecatcherType));
space += size + 2*sizeof(eyecatcherType);
*(eyecatcherType*)(s->ptr) = eyecatcher; /* start eyecatcher */
*(eyecatcherType*)(((char*)(s->ptr)) + (sizeof(eyecatcherType) + size)) = eyecatcher; /* end eyecatcher */
Log(TRACE_MAX, -1, "Allocating %d bytes in heap at file %s line %d ptr %p\n", (int)size, file, line, s->ptr);
TreeAdd(&heap, s, space);
state.current_size += size;
if (state.current_size > state.max_size)
state.max_size = state.current_size;
rc = ((eyecatcherType*)(s->ptr)) + 1; /* skip start eyecatcher */
exit:
Thread_unlock_mutex(heap_mutex);
return rc;
}
static void checkEyecatchers(char* file, int line, void* p, size_t size)
{
eyecatcherType *sp = (eyecatcherType*)p;
char *cp = (char*)p;
eyecatcherType us;
static const char *msg = "Invalid %s eyecatcher %d in heap item at file %s line %d";
if ((us = *--sp) != eyecatcher)
Log(LOG_ERROR, 13, msg, "start", us, file, line);
cp += size;
if ((us = *(eyecatcherType*)cp) != eyecatcher)
Log(LOG_ERROR, 13, msg, "end", us, file, line);
}
/**
* Remove an item from the recorded heap without actually freeing it.
* Use sparingly!
* @param file use the __FILE__ macro to indicate which file this item was allocated in
* @param line use the __LINE__ macro to indicate which line this item was allocated at
* @param p pointer to the item to be removed
*/
static int Internal_heap_unlink(char* file, int line, void* p)
{
Node* e = NULL;
int rc = 0;
e = TreeFind(&heap, ((eyecatcherType*)p)-1);
if (e == NULL)
Log(LOG_ERROR, 13, "Failed to remove heap item at file %s line %d", file, line);
else
{
storageElement* s = (storageElement*)(e->content);
Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes\n",
(int)s->size, file, line, (int)state.current_size);
checkEyecatchers(file, line, p, s->size);
/* free(s->ptr); */
free(s->file);
state.current_size -= s->size;
TreeRemoveNodeIndex(&heap, e, 0);
free(s);
rc = 1;
}
return rc;
}
/**
* Frees a block of memory. A direct replacement for free, but checks that a item is in
* the allocates list first.
* @param file use the __FILE__ macro to indicate which file this item was allocated in
* @param line use the __LINE__ macro to indicate which line this item was allocated at
* @param p pointer to the item to be freed
*/
void myfree(char* file, int line, void* p)
{
if (p) /* it is legal und usual to call free(NULL) */
{
Thread_lock_mutex(heap_mutex);
if (Internal_heap_unlink(file, line, p))
free(((eyecatcherType*)p)-1);
Thread_unlock_mutex(heap_mutex);
}
else
{
Log(LOG_ERROR, -1, "Call of free(NULL) in %s,%d",file,line);
}
}
/**
* Remove an item from the recorded heap without actually freeing it.
* Use sparingly!
* @param file use the __FILE__ macro to indicate which file this item was allocated in
* @param line use the __LINE__ macro to indicate which line this item was allocated at
* @param p pointer to the item to be removed
*/
void Heap_unlink(char* file, int line, void* p)
{
Thread_lock_mutex(heap_mutex);
Internal_heap_unlink(file, line, p);
Thread_unlock_mutex(heap_mutex);
}
/**
* Reallocates a block of memory. A direct replacement for realloc, but keeps track of items
* allocated in a list, so that free can check that a item is being freed correctly and that
* we can check that all memory is freed at shutdown.
* We have to remove the item from the tree, as the memory is in order and so it needs to
* be reinserted in the correct place.
* @param file use the __FILE__ macro to indicate which file this item was reallocated in
* @param line use the __LINE__ macro to indicate which line this item was reallocated at
* @param p pointer to the item to be reallocated
* @param size the new size of the item
* @return pointer to the allocated item, or NULL if there was an error
*/
void *myrealloc(char* file, int line, void* p, size_t size)
{
void* rc = NULL;
storageElement* s = NULL;
Thread_lock_mutex(heap_mutex);
s = TreeRemoveKey(&heap, ((eyecatcherType*)p)-1);
if (s == NULL)
Log(LOG_ERROR, 13, "Failed to reallocate heap item at file %s line %d", file, line);
else
{
size_t space = sizeof(storageElement);
size_t filenamelen = strlen(file)+1;
checkEyecatchers(file, line, p, s->size);
size = Heap_roundup(size);
state.current_size += size - s->size;
if (state.current_size > state.max_size)
state.max_size = state.current_size;
if ((s->ptr = realloc(s->ptr, size + 2*sizeof(eyecatcherType))) == NULL)
{
Log(LOG_ERROR, 13, errmsg);
goto exit;
}
space += size + 2*sizeof(eyecatcherType) - s->size;
*(eyecatcherType*)(s->ptr) = eyecatcher; /* start eyecatcher */
*(eyecatcherType*)(((char*)(s->ptr)) + (sizeof(eyecatcherType) + size)) = eyecatcher; /* end eyecatcher */
s->size = size;
space -= strlen(s->file);
s->file = realloc(s->file, filenamelen);
space += filenamelen;
strcpy(s->file, file);
s->line = line;
rc = s->ptr;
TreeAdd(&heap, s, space);
}
exit:
Thread_unlock_mutex(heap_mutex);
return (rc == NULL) ? NULL : ((eyecatcherType*)(rc)) + 1; /* skip start eyecatcher */
}
/**
* Utility to find an item in the heap. Lets you know if the heap already contains
* the memory location in question.
* @param p pointer to a memory location
* @return pointer to the storage element if found, or NULL
*/
void* Heap_findItem(void* p)
{
Node* e = NULL;
Thread_lock_mutex(heap_mutex);
e = TreeFind(&heap, ((eyecatcherType*)p)-1);
Thread_unlock_mutex(heap_mutex);
return (e == NULL) ? NULL : e->content;
}
/**
* Scans the heap and reports any items currently allocated.
* To be used at shutdown if any heap items have not been freed.
*/
static void HeapScan(enum LOG_LEVELS log_level)
{
Node* current = NULL;
Thread_lock_mutex(heap_mutex);
Log(log_level, -1, "Heap scan start, total %d bytes", (int)state.current_size);
while ((current = TreeNextElement(&heap, current)) != NULL)
{
storageElement* s = (storageElement*)(current->content);
Log(log_level, -1, "Heap element size %d, line %d, file %s, ptr %p", (int)s->size, s->line, s->file, s->ptr);
Log(log_level, -1, " Content %.*s", (10 > current->size) ? (int)s->size : 10, (char*)(((eyecatcherType*)s->ptr) + 1));
#if defined(HEAP_STACK)
Log(log_level, -1, " Stack:\n%s", s->stack);
#endif
}
Log(log_level, -1, "Heap scan end");
Thread_unlock_mutex(heap_mutex);
}
/**
* Heap initialization.
*/
int Heap_initialize(void)
{
TreeInitializeNoMalloc(&heap, ptrCompare);
heap.heap_tracking = 0; /* no recursive heap tracking! */
return 0;
}
/**
* Heap termination.
*/
void Heap_terminate(void)
{
Log(TRACE_MIN, -1, "Maximum heap use was %d bytes", (int)state.max_size);
if (state.current_size > 20) /* One log list is freed after this function is called */
{
Log(LOG_ERROR, -1, "Some memory not freed at shutdown, possible memory leak");
HeapScan(LOG_ERROR);
}
}
/**
* Access to heap state
* @return pointer to the heap state structure
*/
heap_info* Heap_get_info(void)
{
return &state;
}
/**
* Dump a string from the heap so that it can be displayed conveniently
* @param file file handle to dump the heap contents to
* @param str the string to dump, could be NULL
*/
int HeapDumpString(FILE* file, char* str)
{
int rc = 0;
size_t len = str ? strlen(str) + 1 : 0; /* include the trailing null */
if (fwrite(&(str), sizeof(char*), 1, file) != 1)
rc = -1;
else if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
rc = -1;
else if (len > 0 && fwrite(str, len, 1, file) != 1)
rc = -1;
return rc;
}
/**
* Dump the state of the heap
* @param file file handle to dump the heap contents to
*/
int HeapDump(FILE* file)
{
int rc = 0;
Node* current = NULL;
while (rc == 0 && (current = TreeNextElement(&heap, current)))
{
storageElement* s = (storageElement*)(current->content);
if (fwrite(&(s->ptr), sizeof(s->ptr), 1, file) != 1)
rc = -1;
else if (fwrite(&(current->size), sizeof(current->size), 1, file) != 1)
rc = -1;
else if (fwrite(s->ptr, current->size, 1, file) != 1)
rc = -1;
}
return rc;
}
#endif
#if defined(HEAP_UNIT_TESTS)
void Log(enum LOG_LEVELS log_level, int msgno, char* format, ...)
{
printf("Log %s", format);
}
char* Broker_recordFFDC(char* symptoms)
{
printf("recordFFDC");
return "";
}
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
#define free(x) myfree(__FILE__, __LINE__, x)
int main(int argc, char *argv[])
{
char* h = NULL;
Heap_initialize();
h = malloc(12);
free(h);
printf("freed h\n");
h = malloc(12);
h = realloc(h, 14);
h = realloc(h, 25);
h = realloc(h, 255);
h = realloc(h, 2225);
h = realloc(h, 22225);
printf("freeing h\n");
free(h);
Heap_terminate();
printf("Finishing\n");
return 0;
}
#endif /* HEAP_UNIT_TESTS */
/* Local Variables: */
/* indent-tabs-mode: t */
/* c-basic-offset: 8 */
/* End: */

508
deps/paho.mqtt.c/src/LinkedList.c vendored Normal file
View File

@ -0,0 +1,508 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - updates for the async client
*******************************************************************************/
/**
* @file
* \brief functions which apply to linked list structures.
*
* These linked lists can hold data of any sort, pointed to by the content pointer of the
* ListElement structure. ListElements hold the points to the next and previous items in the
* list.
* */
#include "LinkedList.h"
#include <stdlib.h>
#include <string.h>
#include "Heap.h"
static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent);
/**
* Sets a list structure to empty - all null values. Does not remove any items from the list.
* @param newl a pointer to the list structure to be initialized
*/
void ListZero(List* newl)
{
memset(newl, '\0', sizeof(List));
}
/**
* Allocates and initializes a new list structure.
* @return a pointer to the new list structure
*/
List* ListInitialize(void)
{
List* newl = malloc(sizeof(List));
if (newl)
ListZero(newl);
return newl;
}
/**
* Append an already allocated ListElement and content to a list. Can be used to move
* an item from one list to another.
* @param aList the list to which the item is to be added
* @param content the list item content itself
* @param newel the ListElement to be used in adding the new item
* @param size the size of the element
*/
void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size)
{ /* for heap use */
newel->content = content;
newel->next = NULL;
newel->prev = aList->last;
if (aList->first == NULL)
aList->first = newel;
else
aList->last->next = newel;
aList->last = newel;
++(aList->count);
aList->size += size;
}
/**
* Append an item to a list.
* @param aList the list to which the item is to be added
* @param content the list item content itself
* @param size the size of the element
*/
ListElement* ListAppend(List* aList, void* content, size_t size)
{
ListElement* newel = malloc(sizeof(ListElement));
if (newel)
ListAppendNoMalloc(aList, content, newel, size);
return newel;
}
/**
* Insert an item to a list at a specific position.
* @param aList the list to which the item is to be added
* @param content the list item content itself
* @param size the size of the element
* @param index the position in the list. If NULL, this function is equivalent
* to ListAppend.
*/
ListElement* ListInsert(List* aList, void* content, size_t size, ListElement* index)
{
ListElement* newel = malloc(sizeof(ListElement));
if (newel == NULL)
return newel;
if ( index == NULL )
ListAppendNoMalloc(aList, content, newel, size);
else
{
newel->content = content;
newel->next = index;
newel->prev = index->prev;
index->prev = newel;
if ( newel->prev != NULL )
newel->prev->next = newel;
else
aList->first = newel;
++(aList->count);
aList->size += size;
}
return newel;
}
/**
* Finds an element in a list by comparing the content pointers, rather than the contents
* @param aList the list in which the search is to be conducted
* @param content pointer to the list item content itself
* @return the list item found, or NULL
*/
ListElement* ListFind(List* aList, void* content)
{
return ListFindItem(aList, content, NULL);
}
/**
* Finds an element in a list by comparing the content or pointer to the content. A callback
* function is used to define the method of comparison for each element.
* @param aList the list in which the search is to be conducted
* @param content pointer to the content to look for
* @param callback pointer to a function which compares each element (NULL means compare by content pointer)
* @return the list element found, or NULL
*/
ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*))
{
ListElement* rc = NULL;
if (aList->current != NULL && ((callback == NULL && aList->current->content == content) ||
(callback != NULL && callback(aList->current->content, content))))
rc = aList->current;
else
{
ListElement* current = NULL;
/* find the content */
while (ListNextElement(aList, &current) != NULL)
{
if (callback == NULL)
{
if (current->content == content)
{
rc = current;
break;
}
}
else
{
if (callback(current->content, content))
{
rc = current;
break;
}
}
}
if (rc != NULL)
aList->current = rc;
}
return rc;
}
/**
* Removes and optionally frees an element in a list by comparing the content.
* A callback function is used to define the method of comparison for each element.
* @param aList the list in which the search is to be conducted
* @param content pointer to the content to look for
* @param callback pointer to a function which compares each element
* @param freeContent boolean value to indicate whether the item found is to be freed
* @return 1=item removed, 0=item not removed
*/
static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent)
{
ListElement* next = NULL;
ListElement* saved = aList->current;
int saveddeleted = 0;
if (!ListFindItem(aList, content, callback))
return 0; /* false, did not remove item */
if (aList->current->prev == NULL)
/* so this is the first element, and we have to update the "first" pointer */
aList->first = aList->current->next;
else
aList->current->prev->next = aList->current->next;
if (aList->current->next == NULL)
aList->last = aList->current->prev;
else
aList->current->next->prev = aList->current->prev;
next = aList->current->next;
if (freeContent)
{
free(aList->current->content);
aList->current->content = NULL;
}
if (saved == aList->current)
saveddeleted = 1;
free(aList->current);
if (saveddeleted)
aList->current = next;
else
aList->current = saved;
--(aList->count);
return 1; /* successfully removed item */
}
/**
* Removes but does not free an item in a list by comparing the pointer to the content.
* @param aList the list in which the search is to be conducted
* @param content pointer to the content to look for
* @return 1=item removed, 0=item not removed
*/
int ListDetach(List* aList, void* content)
{
return ListUnlink(aList, content, NULL, 0);
}
/**
* Removes and frees an item in a list by comparing the pointer to the content.
* @param aList the list from which the item is to be removed
* @param content pointer to the content to look for
* @return 1=item removed, 0=item not removed
*/
int ListRemove(List* aList, void* content)
{
return ListUnlink(aList, content, NULL, 1);
}
/**
* Removes and frees an the first item in a list.
* @param aList the list from which the item is to be removed
* @return 1=item removed, 0=item not removed
*/
void* ListDetachHead(List* aList)
{
void *content = NULL;
if (aList->count > 0)
{
ListElement* first = aList->first;
if (aList->current == first)
aList->current = first->next;
if (aList->last == first) /* i.e. no of items in list == 1 */
aList->last = NULL;
content = first->content;
aList->first = aList->first->next;
if (aList->first)
aList->first->prev = NULL;
free(first);
--(aList->count);
}
return content;
}
/**
* Removes and frees an the first item in a list.
* @param aList the list from which the item is to be removed
* @return 1=item removed, 0=item not removed
*/
int ListRemoveHead(List* aList)
{
free(ListDetachHead(aList));
return 0;
}
/**
* Removes but does not free the last item in a list.
* @param aList the list from which the item is to be removed
* @return the last item removed (or NULL if none was)
*/
void* ListPopTail(List* aList)
{
void* content = NULL;
if (aList->count > 0)
{
ListElement* last = aList->last;
if (aList->current == last)
aList->current = last->prev;
if (aList->first == last) /* i.e. no of items in list == 1 */
aList->first = NULL;
content = last->content;
aList->last = aList->last->prev;
if (aList->last)
aList->last->next = NULL;
free(last);
--(aList->count);
}
return content;
}
/**
* Removes but does not free an element in a list by comparing the content.
* A callback function is used to define the method of comparison for each element.
* @param aList the list in which the search is to be conducted
* @param content pointer to the content to look for
* @param callback pointer to a function which compares each element
* @return 1=item removed, 0=item not removed
*/
int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*))
{ /* do not free the content */
return ListUnlink(aList, content, callback, 0);
}
/**
* Removes and frees an element in a list by comparing the content.
* A callback function is used to define the method of comparison for each element
* @param aList the list in which the search is to be conducted
* @param content pointer to the content to look for
* @param callback pointer to a function which compares each element
* @return 1=item removed, 0=item not removed
*/
int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*))
{ /* remove from list and free the content */
return ListUnlink(aList, content, callback, 1);
}
/**
* Removes and frees all items in a list, leaving the list ready for new items.
* @param aList the list to which the operation is to be applied
*/
void ListEmpty(List* aList)
{
while (aList->first != NULL)
{
ListElement* first = aList->first;
if (first->content != NULL)
{
free(first->content);
first->content = NULL;
}
aList->first = first->next;
free(first);
}
aList->count = 0;
aList->size = 0;
aList->current = aList->first = aList->last = NULL;
}
/**
* Removes and frees all items in a list, and frees the list itself
* @param aList the list to which the operation is to be applied
*/
void ListFree(List* aList)
{
ListEmpty(aList);
free(aList);
}
/**
* Removes and but does not free all items in a list, and frees the list itself
* @param aList the list to which the operation is to be applied
*/
void ListFreeNoContent(List* aList)
{
while (aList->first != NULL)
{
ListElement* first = aList->first;
aList->first = first->next;
free(first);
}
free(aList);
}
/**
* Forward iteration through a list
* @param aList the list to which the operation is to be applied
* @param pos pointer to the current position in the list. NULL means start from the beginning of the list
* This is updated on return to the same value as that returned from this function
* @return pointer to the current list element
*/
ListElement* ListNextElement(List* aList, ListElement** pos)
{
return *pos = (*pos == NULL) ? aList->first : (*pos)->next;
}
/**
* Backward iteration through a list
* @param aList the list to which the operation is to be applied
* @param pos pointer to the current position in the list. NULL means start from the end of the list
* This is updated on return to the same value as that returned from this function
* @return pointer to the current list element
*/
ListElement* ListPrevElement(List* aList, ListElement** pos)
{
return *pos = (*pos == NULL) ? aList->last : (*pos)->prev;
}
/**
* List callback function for comparing integers
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int intcompare(void* a, void* b)
{
return *((int*)a) == *((int*)b);
}
/**
* List callback function for comparing C strings
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int stringcompare(void* a, void* b)
{
return strcmp((char*)a, (char*)b) == 0;
}
#if defined(UNIT_TESTS)
int main(int argc, char *argv[])
{
int i, *ip, *todelete;
ListElement* current = NULL;
List* l = ListInitialize();
printf("List initialized\n");
for (i = 0; i < 10; i++)
{
ip = malloc(sizeof(int));
*ip = i;
ListAppend(l, (void*)ip, sizeof(int));
if (i==5)
todelete = ip;
printf("List element appended %d\n", *((int*)(l->last->content)));
}
printf("List contents:\n");
current = NULL;
while (ListNextElement(l, &current) != NULL)
printf("List element: %d\n", *((int*)(current->content)));
printf("List contents in reverse order:\n");
current = NULL;
while (ListPrevElement(l, &current) != NULL)
printf("List element: %d\n", *((int*)(current->content)));
/* if ListFindItem(l, *ip, intcompare)->content */
printf("List contents having deleted element %d:\n", *todelete);
ListRemove(l, todelete);
current = NULL;
while (ListNextElement(l, &current) != NULL)
printf("List element: %d\n", *((int*)(current->content)));
i = 9;
ListRemoveItem(l, &i, intcompare);
printf("List contents having deleted another element, %d, size now %d:\n", i, l->size);
current = NULL;
while (ListNextElement(l, &current) != NULL)
printf("List element: %d\n", *((int*)(current->content)));
ListFree(l);
printf("List freed\n");
}
#endif

577
deps/paho.mqtt.c/src/Log.c vendored Normal file
View File

@ -0,0 +1,577 @@
/*******************************************************************************
* 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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - updates for the async client
* Ian Craggs - fix for bug #427028
*******************************************************************************/
/**
* @file
* \brief Logging and tracing module
*
*
*/
#include "Log.h"
#include "MQTTPacket.h"
#include "MQTTProtocol.h"
#include "MQTTProtocolClient.h"
#include "Messages.h"
#include "LinkedList.h"
#include "StackTrace.h"
#include "Thread.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
#if !defined(_WIN32) && !defined(_WIN64)
#include <syslog.h>
#include <sys/stat.h>
#define GETTIMEOFDAY 1
#else
#define snprintf _snprintf
#endif
#if defined(GETTIMEOFDAY)
#include <sys/time.h>
#else
#include <sys/timeb.h>
#endif
#if !defined(_WIN32) && !defined(_WIN64)
/**
* _unlink mapping for linux
*/
#define _unlink unlink
#endif
#if !defined(min)
#define min(A,B) ( (A) < (B) ? (A):(B))
#endif
trace_settings_type trace_settings =
{
TRACE_MINIMUM,
400,
INVALID_LEVEL
};
#define MAX_FUNCTION_NAME_LENGTH 256
typedef struct
{
#if defined(GETTIMEOFDAY)
struct timeval ts;
#else
struct timeb ts;
#endif
int sametime_count;
int number;
int thread_id;
int depth;
char name[MAX_FUNCTION_NAME_LENGTH + 1];
int line;
int has_rc;
int rc;
enum LOG_LEVELS level;
} traceEntry;
static int start_index = -1,
next_index = 0;
static traceEntry* trace_queue = NULL;
static int trace_queue_size = 0;
static FILE* trace_destination = NULL; /**< flag to indicate if trace is to be sent to a stream */
static char* trace_destination_name = NULL; /**< the name of the trace file */
static char* trace_destination_backup_name = NULL; /**< the name of the backup trace file */
static int lines_written = 0; /**< number of lines written to the current output file */
static int max_lines_per_file = 1000; /**< maximum number of lines to write to one trace file */
static enum LOG_LEVELS trace_output_level = INVALID_LEVEL;
static Log_traceCallback* trace_callback = NULL;
static traceEntry* Log_pretrace(void);
static char* Log_formatTraceEntry(traceEntry* cur_entry);
static void Log_output(enum LOG_LEVELS log_level, const char *msg);
static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry);
static void Log_trace(enum LOG_LEVELS log_level, const char *buf);
#if 0
static FILE* Log_destToFile(const char *dest);
static int Log_compareEntries(const char *entry1, const char *entry2);
#endif
static int sametime_count = 0;
#if defined(GETTIMEOFDAY)
struct timeval ts, last_ts;
#else
struct timeb ts, last_ts;
#endif
static char msg_buf[512];
#if defined(_WIN32) || defined(_WIN64)
mutex_type log_mutex;
#else
static pthread_mutex_t log_mutex_store = PTHREAD_MUTEX_INITIALIZER;
static mutex_type log_mutex = &log_mutex_store;
#endif
int Log_initialize(Log_nameValue* info)
{
int rc = SOCKET_ERROR;
char* envval = NULL;
#if !defined(_WIN32) && !defined(_WIN64)
struct stat buf;
#endif
if ((trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries)) == NULL)
goto exit;
trace_queue_size = trace_settings.max_trace_entries;
if ((envval = getenv("MQTT_C_CLIENT_TRACE")) != NULL && strlen(envval) > 0)
{
if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "w")) == NULL)
trace_destination = stdout;
else
{
if ((trace_destination_name = malloc(strlen(envval) + 1)) == NULL)
{
free(trace_queue);
goto exit;
}
strcpy(trace_destination_name, envval);
if ((trace_destination_backup_name = malloc(strlen(envval) + 3)) == NULL)
{
free(trace_queue);
free(trace_destination_name);
goto exit;
}
sprintf(trace_destination_backup_name, "%s.0", trace_destination_name);
}
}
if ((envval = getenv("MQTT_C_CLIENT_TRACE_MAX_LINES")) != NULL && strlen(envval) > 0)
{
max_lines_per_file = atoi(envval);
if (max_lines_per_file <= 0)
max_lines_per_file = 1000;
}
if ((envval = getenv("MQTT_C_CLIENT_TRACE_LEVEL")) != NULL && strlen(envval) > 0)
{
if (strcmp(envval, "MAXIMUM") == 0 || strcmp(envval, "TRACE_MAXIMUM") == 0)
trace_settings.trace_level = TRACE_MAXIMUM;
else if (strcmp(envval, "MEDIUM") == 0 || strcmp(envval, "TRACE_MEDIUM") == 0)
trace_settings.trace_level = TRACE_MEDIUM;
else if (strcmp(envval, "MINIMUM") == 0 || strcmp(envval, "TRACE_MINIMUM") == 0)
trace_settings.trace_level = TRACE_MINIMUM;
else if (strcmp(envval, "PROTOCOL") == 0 || strcmp(envval, "TRACE_PROTOCOL") == 0)
trace_output_level = TRACE_PROTOCOL;
else if (strcmp(envval, "ERROR") == 0 || strcmp(envval, "TRACE_ERROR") == 0)
trace_output_level = LOG_ERROR;
}
Log_output(TRACE_MINIMUM, "=========================================================");
Log_output(TRACE_MINIMUM, " Trace Output");
if (info)
{
while (info->name)
{
snprintf(msg_buf, sizeof(msg_buf), "%s: %s", info->name, info->value);
Log_output(TRACE_MINIMUM, msg_buf);
info++;
}
}
#if !defined(_WIN32) && !defined(_WIN64)
if (stat("/proc/version", &buf) != -1)
{
FILE* vfile;
if ((vfile = fopen("/proc/version", "r")) != NULL)
{
int len;
strcpy(msg_buf, "/proc/version: ");
len = strlen(msg_buf);
if (fgets(&msg_buf[len], sizeof(msg_buf) - len, vfile))
Log_output(TRACE_MINIMUM, msg_buf);
fclose(vfile);
}
}
#endif
Log_output(TRACE_MINIMUM, "=========================================================");
exit:
return rc;
}
void Log_setTraceCallback(Log_traceCallback* callback)
{
trace_callback = callback;
}
void Log_setTraceLevel(enum LOG_LEVELS level)
{
if (level < TRACE_MINIMUM) /* the lowest we can go is TRACE_MINIMUM*/
trace_settings.trace_level = level;
trace_output_level = level;
}
void Log_terminate(void)
{
free(trace_queue);
trace_queue = NULL;
trace_queue_size = 0;
if (trace_destination)
{
if (trace_destination != stdout)
fclose(trace_destination);
trace_destination = NULL;
}
if (trace_destination_name) {
free(trace_destination_name);
trace_destination_name = NULL;
}
if (trace_destination_backup_name) {
free(trace_destination_backup_name);
trace_destination_backup_name = NULL;
}
start_index = -1;
next_index = 0;
trace_output_level = INVALID_LEVEL;
sametime_count = 0;
}
static traceEntry* Log_pretrace(void)
{
traceEntry *cur_entry = NULL;
/* calling ftime/gettimeofday seems to be comparatively expensive, so we need to limit its use */
if (++sametime_count % 20 == 0)
{
#if defined(GETTIMEOFDAY)
gettimeofday(&ts, NULL);
if (ts.tv_sec != last_ts.tv_sec || ts.tv_usec != last_ts.tv_usec)
#else
ftime(&ts);
if (ts.time != last_ts.time || ts.millitm != last_ts.millitm)
#endif
{
sametime_count = 0;
last_ts = ts;
}
}
if (trace_queue_size != trace_settings.max_trace_entries)
{
traceEntry* new_trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries);
if (new_trace_queue == NULL)
goto exit;
memcpy(new_trace_queue, trace_queue, min(trace_queue_size, trace_settings.max_trace_entries) * sizeof(traceEntry));
free(trace_queue);
trace_queue = new_trace_queue;
trace_queue_size = trace_settings.max_trace_entries;
if (start_index > trace_settings.max_trace_entries + 1 ||
next_index > trace_settings.max_trace_entries + 1)
{
start_index = -1;
next_index = 0;
}
}
/* add to trace buffer */
cur_entry = &trace_queue[next_index];
if (next_index == start_index) /* means the buffer is full */
{
if (++start_index == trace_settings.max_trace_entries)
start_index = 0;
} else if (start_index == -1)
start_index = 0;
if (++next_index == trace_settings.max_trace_entries)
next_index = 0;
exit:
return cur_entry;
}
static char* Log_formatTraceEntry(traceEntry* cur_entry)
{
struct tm *timeinfo;
int buf_pos = 31;
#if defined(GETTIMEOFDAY)
timeinfo = localtime((time_t *)&cur_entry->ts.tv_sec);
#else
timeinfo = localtime(&cur_entry->ts.time);
#endif
strftime(&msg_buf[7], 80, "%Y%m%d %H%M%S ", timeinfo);
#if defined(GETTIMEOFDAY)
sprintf(&msg_buf[22], ".%.3lu ", cur_entry->ts.tv_usec / 1000L);
#else
sprintf(&msg_buf[22], ".%.3hu ", cur_entry->ts.millitm);
#endif
buf_pos = 27;
sprintf(msg_buf, "(%.4d)", cur_entry->sametime_count);
msg_buf[6] = ' ';
if (cur_entry->has_rc == 2)
strncpy(&msg_buf[buf_pos], cur_entry->name, sizeof(msg_buf)-buf_pos);
else
{
const char *format = Messages_get(cur_entry->number, cur_entry->level);
if (cur_entry->has_rc == 1)
snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line, cur_entry->rc);
else
snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line);
}
return msg_buf;
}
static void Log_output(enum LOG_LEVELS log_level, const char *msg)
{
if (trace_destination)
{
fprintf(trace_destination, "%s\n", msg);
if (trace_destination != stdout && ++lines_written >= max_lines_per_file)
{
fclose(trace_destination);
_unlink(trace_destination_backup_name); /* remove any old backup trace file */
rename(trace_destination_name, trace_destination_backup_name); /* rename recently closed to backup */
trace_destination = fopen(trace_destination_name, "w"); /* open new trace file */
if (trace_destination == NULL)
trace_destination = stdout;
lines_written = 0;
}
else
fflush(trace_destination);
}
if (trace_callback)
(*trace_callback)(log_level, msg);
}
static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry)
{
if (((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
{
char* msg = NULL;
if (trace_destination || trace_callback)
msg = &Log_formatTraceEntry(cur_entry)[7];
Log_output(log_level, msg);
}
}
static void Log_trace(enum LOG_LEVELS log_level, const char *buf)
{
traceEntry *cur_entry = NULL;
if (trace_queue == NULL)
return;
cur_entry = Log_pretrace();
memcpy(&(cur_entry->ts), &ts, sizeof(ts));
cur_entry->sametime_count = sametime_count;
cur_entry->has_rc = 2;
strncpy(cur_entry->name, buf, sizeof(cur_entry->name));
cur_entry->name[MAX_FUNCTION_NAME_LENGTH] = '\0';
Log_posttrace(log_level, cur_entry);
}
/**
* Log a message. If possible, all messages should be indexed by message number, and
* the use of the format string should be minimized or negated altogether. If format is
* provided, the message number is only used as a message label.
* @param log_level the log level of the message
* @param msgno the id of the message to use if the format string is NULL
* @param aFormat the printf format string to be used if the message id does not exist
* @param ... the printf inserts
*/
void Log(enum LOG_LEVELS log_level, int msgno, const char *format, ...)
{
if (log_level >= trace_settings.trace_level)
{
const char *temp = NULL;
va_list args;
/* we're using a static character buffer, so we need to make sure only one thread uses it at a time */
Thread_lock_mutex(log_mutex);
if (format == NULL && (temp = Messages_get(msgno, log_level)) != NULL)
format = temp;
va_start(args, format);
vsnprintf(msg_buf, sizeof(msg_buf), format, args);
Log_trace(log_level, msg_buf);
va_end(args);
Thread_unlock_mutex(log_mutex);
}
}
/**
* The reason for this function is to make trace logging as fast as possible so that the
* function exit/entry history can be captured by default without unduly impacting
* performance. Therefore it must do as little as possible.
* @param log_level the log level of the message
* @param msgno the id of the message to use if the format string is NULL
* @param aFormat the printf format string to be used if the message id does not exist
* @param ... the printf inserts
*/
void Log_stackTrace(enum LOG_LEVELS log_level, int msgno, int thread_id, int current_depth, const char* name, int line, int* rc)
{
traceEntry *cur_entry = NULL;
if (trace_queue == NULL)
return;
if (log_level < trace_settings.trace_level)
return;
Thread_lock_mutex(log_mutex);
cur_entry = Log_pretrace();
memcpy(&(cur_entry->ts), &ts, sizeof(ts));
cur_entry->sametime_count = sametime_count;
cur_entry->number = msgno;
cur_entry->thread_id = thread_id;
cur_entry->depth = current_depth;
strcpy(cur_entry->name, name);
cur_entry->level = log_level;
cur_entry->line = line;
if (rc == NULL)
cur_entry->has_rc = 0;
else
{
cur_entry->has_rc = 1;
cur_entry->rc = *rc;
}
Log_posttrace(log_level, cur_entry);
Thread_unlock_mutex(log_mutex);
}
#if 0
static FILE* Log_destToFile(const char *dest)
{
FILE* file = NULL;
if (strcmp(dest, "stdout") == 0)
file = stdout;
else if (strcmp(dest, "stderr") == 0)
file = stderr;
else
{
if (strstr(dest, "FFDC"))
file = fopen(dest, "ab");
else
file = fopen(dest, "wb");
}
return file;
}
static int Log_compareEntries(const char *entry1, const char *entry2)
{
int comp = strncmp(&entry1[7], &entry2[7], 19);
/* if timestamps are equal, use the sequence numbers */
if (comp == 0)
comp = strncmp(&entry1[1], &entry2[1], 4);
return comp;
}
/**
* Write the contents of the stored trace to a stream
* @param dest string which contains a file name or the special strings stdout or stderr
*/
int Log_dumpTrace(char* dest)
{
FILE* file = NULL;
ListElement* cur_trace_entry = NULL;
const int msgstart = 7;
int rc = -1;
int trace_queue_index = 0;
if ((file = Log_destToFile(dest)) == NULL)
{
Log(LOG_ERROR, 9, NULL, "trace", dest, "trace entries");
goto exit;
}
fprintf(file, "=========== Start of trace dump ==========\n");
/* Interleave the log and trace entries together appropriately */
ListNextElement(trace_buffer, &cur_trace_entry);
trace_queue_index = start_index;
if (trace_queue_index == -1)
trace_queue_index = next_index;
else
{
Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
if (trace_queue_index == trace_settings.max_trace_entries)
trace_queue_index = 0;
}
while (cur_trace_entry || trace_queue_index != next_index)
{
if (cur_trace_entry && trace_queue_index != -1)
{ /* compare these timestamps */
if (Log_compareEntries((char*)cur_trace_entry->content, msg_buf) > 0)
cur_trace_entry = NULL;
}
if (cur_trace_entry)
{
fprintf(file, "%s\n", &((char*)(cur_trace_entry->content))[msgstart]);
ListNextElement(trace_buffer, &cur_trace_entry);
}
else
{
fprintf(file, "%s\n", &msg_buf[7]);
if (trace_queue_index != next_index)
{
Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
if (trace_queue_index == trace_settings.max_trace_entries)
trace_queue_index = 0;
}
}
}
fprintf(file, "========== End of trace dump ==========\n\n");
if (file != stdout && file != stderr && file != NULL)
fclose(file);
rc = 0;
exit:
return rc;
}
#endif

4459
deps/paho.mqtt.c/src/MQTTAsync.c vendored Normal file

File diff suppressed because it is too large Load Diff

2980
deps/paho.mqtt.c/src/MQTTClient.c vendored Normal file

File diff suppressed because it is too large Load Diff

1041
deps/paho.mqtt.c/src/MQTTPacket.c vendored Normal file

File diff suppressed because it is too large Load Diff

464
deps/paho.mqtt.c/src/MQTTPacketOut.c vendored Normal file
View File

@ -0,0 +1,464 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
* Ian Craggs - MQTT 3.1.1 support
* Rong Xiang, Ian Craggs - C++ compatibility
* Ian Craggs - binary password and will payload
* Ian Craggs - MQTT 5.0 support
*******************************************************************************/
/**
* @file
* \brief functions to deal with reading and writing of MQTT packets from and to sockets
*
* Some other related functions are in the MQTTPacket module
*/
#include "MQTTPacketOut.h"
#include "Log.h"
#include "StackTrace.h"
#include <string.h>
#include <stdlib.h>
#include "Heap.h"
/**
* Send an MQTT CONNECT packet down a socket for V5 or later
* @param client a structure from which to get all the required values
* @param MQTTVersion the MQTT version to connect with
* @param connectProperties MQTT V5 properties for the connect packet
* @param willProperties MQTT V5 properties for the will message, if any
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_connect(Clients* client, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties)
{
char *buf, *ptr;
Connect packet;
int rc = SOCKET_ERROR, len;
FUNC_ENTRY;
packet.header.byte = 0;
packet.header.bits.type = CONNECT;
len = ((MQTTVersion == MQTTVERSION_3_1) ? 12 : 10) + (int)strlen(client->clientID)+2;
if (client->will)
len += (int)strlen(client->will->topic)+2 + client->will->payloadlen+2;
if (client->username)
len += (int)strlen(client->username)+2;
if (client->password)
len += client->passwordlen+2;
if (MQTTVersion >= MQTTVERSION_5)
{
len += MQTTProperties_len(connectProperties);
if (client->will)
len += MQTTProperties_len(willProperties);
}
ptr = buf = malloc(len);
if (ptr == NULL)
goto exit_nofree;
if (MQTTVersion == MQTTVERSION_3_1)
{
writeUTF(&ptr, "MQIsdp");
writeChar(&ptr, (char)MQTTVERSION_3_1);
}
else if (MQTTVersion == MQTTVERSION_3_1_1 || MQTTVersion == MQTTVERSION_5)
{
writeUTF(&ptr, "MQTT");
writeChar(&ptr, (char)MQTTVersion);
}
else
goto exit;
packet.flags.all = 0;
if (MQTTVersion >= MQTTVERSION_5)
packet.flags.bits.cleanstart = client->cleanstart;
else
packet.flags.bits.cleanstart = client->cleansession;
packet.flags.bits.will = (client->will) ? 1 : 0;
if (packet.flags.bits.will)
{
packet.flags.bits.willQoS = client->will->qos;
packet.flags.bits.willRetain = client->will->retained;
}
if (client->username)
packet.flags.bits.username = 1;
if (client->password)
packet.flags.bits.password = 1;
writeChar(&ptr, packet.flags.all);
writeInt(&ptr, client->keepAliveInterval);
if (MQTTVersion >= MQTTVERSION_5)
MQTTProperties_write(&ptr, connectProperties);
writeUTF(&ptr, client->clientID);
if (client->will)
{
if (MQTTVersion >= MQTTVERSION_5)
MQTTProperties_write(&ptr, willProperties);
writeUTF(&ptr, client->will->topic);
writeData(&ptr, client->will->payload, client->will->payloadlen);
}
if (client->username)
writeUTF(&ptr, client->username);
if (client->password)
writeData(&ptr, client->password, client->passwordlen);
rc = MQTTPacket_send(&client->net, packet.header, buf, len, 1, MQTTVersion);
Log(LOG_PROTOCOL, 0, NULL, client->net.socket, client->clientID,
MQTTVersion, client->cleansession, rc);
exit:
if (rc != TCPSOCKET_INTERRUPTED)
free(buf);
exit_nofree:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Function used in the new packets table to create connack packets.
* @param MQTTVersion MQTT 5 or less?
* @param aHeader the MQTT header byte
* @param data the rest of the packet
* @param datalen the length of the rest of the packet
* @return pointer to the packet structure
*/
void* MQTTPacket_connack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
{
Connack* pack = NULL;
char* curdata = data;
char* enddata = &data[datalen];
FUNC_ENTRY;
if ((pack = malloc(sizeof(Connack))) == NULL)
goto exit;
pack->MQTTVersion = MQTTVersion;
pack->header.byte = aHeader;
pack->flags.all = readChar(&curdata); /* connect flags */
pack->rc = readChar(&curdata); /* reason code */
if (MQTTVersion < MQTTVERSION_5)
{
if (datalen != 2)
{
free(pack);
pack = NULL;
}
}
else if (datalen > 2)
{
MQTTProperties props = MQTTProperties_initializer;
pack->properties = props;
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
{
if (pack->properties.array)
free(pack->properties.array);
if (pack)
free(pack);
pack = NULL; /* signal protocol error */
goto exit;
}
}
exit:
FUNC_EXIT;
return pack;
}
/**
* Free allocated storage for a connack packet.
* @param pack pointer to the connack packet structure
*/
void MQTTPacket_freeConnack(Connack* pack)
{
FUNC_ENTRY;
if (pack->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&pack->properties);
free(pack);
FUNC_EXIT;
}
/**
* Send an MQTT PINGREQ packet down a socket.
* @param socket the open socket to send the data to
* @param clientID the string client identifier, only used for tracing
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID)
{
Header header;
int rc = 0;
FUNC_ENTRY;
header.byte = 0;
header.bits.type = PINGREQ;
rc = MQTTPacket_send(net, header, NULL, 0, 0, MQTTVERSION_3_1_1);
Log(LOG_PROTOCOL, 20, NULL, net->socket, clientID, rc);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Send an MQTT subscribe packet down a socket.
* @param topics list of topics
* @param qoss list of corresponding QoSs
* @param msgid the MQTT message id to use
* @param dup boolean - whether to set the MQTT DUP flag
* @param socket the open socket to send the data to
* @param clientID the string client identifier, only used for tracing
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_subscribe(List* topics, List* qoss, MQTTSubscribe_options* opts, MQTTProperties* props,
int msgid, int dup, Clients* client)
{
Header header;
char *data, *ptr;
int rc = -1;
ListElement *elem = NULL, *qosElem = NULL;
int datalen, i = 0;
FUNC_ENTRY;
header.bits.type = SUBSCRIBE;
header.bits.dup = dup;
header.bits.qos = 1;
header.bits.retain = 0;
datalen = 2 + topics->count * 3; /* utf length + char qos == 3 */
while (ListNextElement(topics, &elem))
datalen += (int)strlen((char*)(elem->content));
if (client->MQTTVersion >= MQTTVERSION_5)
datalen += MQTTProperties_len(props);
ptr = data = malloc(datalen);
if (ptr == NULL)
goto exit;
writeInt(&ptr, msgid);
if (client->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_write(&ptr, props);
elem = NULL;
while (ListNextElement(topics, &elem))
{
char subopts = 0;
ListNextElement(qoss, &qosElem);
writeUTF(&ptr, (char*)(elem->content));
subopts = *(int*)(qosElem->content);
if (client->MQTTVersion >= MQTTVERSION_5 && opts != NULL)
{
subopts |= (opts[i].noLocal << 2); /* 1 bit */
subopts |= (opts[i].retainAsPublished << 3); /* 1 bit */
subopts |= (opts[i].retainHandling << 4); /* 2 bits */
}
writeChar(&ptr, subopts);
++i;
}
rc = MQTTPacket_send(&client->net, header, data, datalen, 1, client->MQTTVersion);
Log(LOG_PROTOCOL, 22, NULL, client->net.socket, client->clientID, msgid, rc);
if (rc != TCPSOCKET_INTERRUPTED)
free(data);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Function used in the new packets table to create suback packets.
* @param MQTTVersion the version of MQTT
* @param aHeader the MQTT header byte
* @param data the rest of the packet
* @param datalen the length of the rest of the packet
* @return pointer to the packet structure
*/
void* MQTTPacket_suback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
{
Suback* pack = NULL;
char* curdata = data;
char* enddata = &data[datalen];
FUNC_ENTRY;
if ((pack = malloc(sizeof(Suback))) == NULL)
goto exit;
pack->MQTTVersion = MQTTVersion;
pack->header.byte = aHeader;
pack->msgId = readInt(&curdata);
if (MQTTVersion >= MQTTVERSION_5)
{
MQTTProperties props = MQTTProperties_initializer;
pack->properties = props;
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
{
if (pack->properties.array)
free(pack->properties.array);
if (pack)
free(pack);
pack = NULL; /* signal protocol error */
goto exit;
}
}
pack->qoss = ListInitialize();
while ((size_t)(curdata - data) < datalen)
{
unsigned int* newint;
newint = malloc(sizeof(unsigned int));
if (newint == NULL)
{
if (pack->properties.array)
free(pack->properties.array);
if (pack)
free(pack);
pack = NULL; /* signal protocol error */
goto exit;
}
*newint = (unsigned int)readChar(&curdata);
ListAppend(pack->qoss, newint, sizeof(unsigned int));
}
if (pack->qoss->count == 0)
{
if (pack->properties.array)
free(pack->properties.array);
if (pack)
free(pack);
ListFree(pack->qoss);
pack = NULL;
}
exit:
FUNC_EXIT;
return pack;
}
/**
* Send an MQTT unsubscribe packet down a socket.
* @param topics list of topics
* @param msgid the MQTT message id to use
* @param dup boolean - whether to set the MQTT DUP flag
* @param socket the open socket to send the data to
* @param clientID the string client identifier, only used for tracing
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_unsubscribe(List* topics, MQTTProperties* props, int msgid, int dup, Clients* client)
{
Header header;
char *data, *ptr;
int rc = SOCKET_ERROR;
ListElement *elem = NULL;
int datalen;
FUNC_ENTRY;
header.bits.type = UNSUBSCRIBE;
header.bits.dup = dup;
header.bits.qos = 1;
header.bits.retain = 0;
datalen = 2 + topics->count * 2; /* utf length == 2 */
while (ListNextElement(topics, &elem))
datalen += (int)strlen((char*)(elem->content));
if (client->MQTTVersion >= MQTTVERSION_5)
datalen += MQTTProperties_len(props);
ptr = data = malloc(datalen);
if (ptr == NULL)
goto exit;
writeInt(&ptr, msgid);
if (client->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_write(&ptr, props);
elem = NULL;
while (ListNextElement(topics, &elem))
writeUTF(&ptr, (char*)(elem->content));
rc = MQTTPacket_send(&client->net, header, data, datalen, 1, client->MQTTVersion);
Log(LOG_PROTOCOL, 25, NULL, client->net.socket, client->clientID, msgid, rc);
if (rc != TCPSOCKET_INTERRUPTED)
free(data);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Function used in the new packets table to create unsuback packets.
* @param MQTTVersion the version of MQTT
* @param aHeader the MQTT header byte
* @param data the rest of the packet
* @param datalen the length of the rest of the packet
* @return pointer to the packet structure
*/
void* MQTTPacket_unsuback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
{
Unsuback* pack = NULL;
char* curdata = data;
char* enddata = &data[datalen];
FUNC_ENTRY;
if ((pack = malloc(sizeof(Unsuback))) == NULL)
goto exit;
pack->MQTTVersion = MQTTVersion;
pack->header.byte = aHeader;
pack->msgId = readInt(&curdata);
pack->reasonCodes = NULL;
if (MQTTVersion >= MQTTVERSION_5)
{
MQTTProperties props = MQTTProperties_initializer;
pack->properties = props;
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
{
if (pack->properties.array)
free(pack->properties.array);
if (pack)
free(pack);
pack = NULL; /* signal protocol error */
goto exit;
}
pack->reasonCodes = ListInitialize();
while ((size_t)(curdata - data) < datalen)
{
enum MQTTReasonCodes* newrc;
newrc = malloc(sizeof(enum MQTTReasonCodes));
if (newrc == NULL)
{
if (pack->properties.array)
free(pack->properties.array);
if (pack)
free(pack);
pack = NULL; /* signal protocol error */
goto exit;
}
*newrc = (enum MQTTReasonCodes)readChar(&curdata);
ListAppend(pack->reasonCodes, newrc, sizeof(enum MQTTReasonCodes));
}
if (pack->reasonCodes->count == 0)
{
ListFree(pack->reasonCodes);
if (pack->properties.array)
free(pack->properties.array);
if (pack)
free(pack);
pack = NULL;
}
}
exit:
FUNC_EXIT;
return pack;
}

827
deps/paho.mqtt.c/src/MQTTPersistence.c vendored Normal file
View File

@ -0,0 +1,827 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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
*******************************************************************************/
/**
* @file
* \brief Functions that apply to persistence operations.
*
*/
#include <stdio.h>
#include <string.h>
#include "MQTTPersistence.h"
#include "MQTTPersistenceDefault.h"
#include "MQTTProtocolClient.h"
#include "Heap.h"
static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen, int MQTTVersion);
static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size);
/**
* Creates a ::MQTTClient_persistence structure representing a persistence implementation.
* @param persistence the ::MQTTClient_persistence structure.
* @param type the type of the persistence implementation. See ::MQTTClient_create.
* @param pcontext the context for this persistence implementation. See ::MQTTClient_create.
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
*/
#include "StackTrace.h"
int MQTTPersistence_create(MQTTClient_persistence** persistence, int type, void* pcontext)
{
int rc = 0;
MQTTClient_persistence* per = NULL;
FUNC_ENTRY;
#if !defined(NO_PERSISTENCE)
switch (type)
{
case MQTTCLIENT_PERSISTENCE_NONE :
per = NULL;
break;
case MQTTCLIENT_PERSISTENCE_DEFAULT :
per = malloc(sizeof(MQTTClient_persistence));
if ( per != NULL )
{
if ( pcontext != NULL )
{
if ((per->context = malloc(strlen(pcontext) + 1)) == NULL)
{
free(per);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy(per->context, pcontext);
}
else
per->context = "."; /* working directory */
/* file system functions */
per->popen = pstopen;
per->pclose = pstclose;
per->pput = pstput;
per->pget = pstget;
per->premove = pstremove;
per->pkeys = pstkeys;
per->pclear = pstclear;
per->pcontainskey = pstcontainskey;
}
else
rc = PAHO_MEMORY_ERROR;
break;
case MQTTCLIENT_PERSISTENCE_USER :
per = (MQTTClient_persistence *)pcontext;
if ( per == NULL || (per != NULL && (per->context == NULL || per->pclear == NULL ||
per->pclose == NULL || per->pcontainskey == NULL || per->pget == NULL || per->pkeys == NULL ||
per->popen == NULL || per->pput == NULL || per->premove == NULL)) )
rc = MQTTCLIENT_PERSISTENCE_ERROR;
break;
default:
rc = MQTTCLIENT_PERSISTENCE_ERROR;
break;
}
#endif
*persistence = per;
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Open persistent store and restore any persisted messages.
* @param client the client as ::Clients.
* @param serverURI the URI of the remote end.
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
*/
int MQTTPersistence_initialize(Clients *c, const char *serverURI)
{
int rc = 0;
FUNC_ENTRY;
if ( c->persistence != NULL )
{
rc = c->persistence->popen(&(c->phandle), c->clientID, serverURI, c->persistence->context);
if ( rc == 0 )
rc = MQTTPersistence_restore(c);
}
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Close persistent store.
* @param client the client as ::Clients.
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
*/
int MQTTPersistence_close(Clients *c)
{
int rc =0;
FUNC_ENTRY;
if (c->persistence != NULL)
{
rc = c->persistence->pclose(c->phandle);
c->phandle = NULL;
#if !defined(NO_PERSISTENCE)
if ( c->persistence->popen == pstopen )
free(c->persistence);
#endif
c->persistence = NULL;
}
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Clears the persistent store.
* @param client the client as ::Clients.
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
*/
int MQTTPersistence_clear(Clients *c)
{
int rc = 0;
FUNC_ENTRY;
if (c->persistence != NULL)
rc = c->persistence->pclear(c->phandle);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Restores the persisted records to the outbound and inbound message queues of the
* client.
* @param client the client as ::Clients.
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
*/
int MQTTPersistence_restore(Clients *c)
{
int rc = 0;
char **msgkeys = NULL,
*buffer = NULL;
int nkeys, buflen;
int i = 0;
int msgs_sent = 0;
int msgs_rcvd = 0;
FUNC_ENTRY;
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
{
while (rc == 0 && i < nkeys)
{
if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) == 0 ||
strncmp(msgkeys[i], PERSISTENCE_V5_COMMAND_KEY, strlen(PERSISTENCE_V5_COMMAND_KEY)) == 0)
{
;
}
else if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) == 0 ||
strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) == 0)
{
;
}
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0)
{
int data_MQTTVersion = MQTTVERSION_3_1_1;
char* cur_key = msgkeys[i];
MQTTPacket* pack = NULL;
if ( strncmp(cur_key, PERSISTENCE_V5_PUBLISH_RECEIVED,
strlen(PERSISTENCE_V5_PUBLISH_RECEIVED)) == 0)
{
data_MQTTVersion = MQTTVERSION_5;
cur_key = PERSISTENCE_PUBLISH_RECEIVED;
}
else if (strncmp(cur_key, PERSISTENCE_V5_PUBLISH_SENT,
strlen(PERSISTENCE_V5_PUBLISH_SENT)) == 0)
{
data_MQTTVersion = MQTTVERSION_5;
cur_key = PERSISTENCE_PUBLISH_SENT;
}
else if (strncmp(cur_key, PERSISTENCE_V5_PUBREL,
strlen(PERSISTENCE_V5_PUBREL)) == 0)
{
data_MQTTVersion = MQTTVERSION_5;
cur_key = PERSISTENCE_PUBREL;
}
if (data_MQTTVersion == MQTTVERSION_5 && c->MQTTVersion < MQTTVERSION_5)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR; /* can't restore version 5 data with a version 3 client */
goto exit;
}
pack = MQTTPersistence_restorePacket(data_MQTTVersion, buffer, buflen);
if ( pack != NULL )
{
if (strncmp(cur_key, PERSISTENCE_PUBLISH_RECEIVED,
strlen(PERSISTENCE_PUBLISH_RECEIVED)) == 0)
{
Publish* publish = (Publish*)pack;
Messages* msg = NULL;
publish->MQTTVersion = c->MQTTVersion;
msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain, 1);
msg->nextMessageType = PUBREL;
/* order does not matter for persisted received messages */
ListAppend(c->inboundMsgs, msg, msg->len);
if (c->MQTTVersion >= MQTTVERSION_5)
{
free(msg->publish->payload);
free(msg->publish->topic);
msg->publish->payload = msg->publish->topic = NULL;
}
publish->topic = NULL;
MQTTPacket_freePublish(publish);
msgs_rcvd++;
}
else if (strncmp(cur_key, PERSISTENCE_PUBLISH_SENT,
strlen(PERSISTENCE_PUBLISH_SENT)) == 0)
{
Publish* publish = (Publish*)pack;
Messages* msg = NULL;
char *key = malloc(MESSAGE_FILENAME_LENGTH + 1);
if (!key)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
publish->MQTTVersion = c->MQTTVersion;
if (publish->MQTTVersion >= MQTTVERSION_5)
sprintf(key, "%s%d", PERSISTENCE_V5_PUBREL, publish->msgId);
else
sprintf(key, "%s%d", PERSISTENCE_PUBREL, publish->msgId);
msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain, 1);
if (c->persistence->pcontainskey(c->phandle, key) == 0)
/* PUBLISH Qo2 and PUBREL sent */
msg->nextMessageType = PUBCOMP;
/* else: PUBLISH QoS1, or PUBLISH QoS2 and PUBREL not sent */
/* retry at the first opportunity */
memset(&msg->lastTouch, '\0', sizeof(msg->lastTouch));
MQTTPersistence_insertInOrder(c->outboundMsgs, msg, msg->len);
publish->topic = NULL;
MQTTPacket_freePublish(publish);
free(key);
msgs_sent++;
}
else if (strncmp(cur_key, PERSISTENCE_PUBREL, strlen(PERSISTENCE_PUBREL)) == 0)
{
/* orphaned PUBRELs ? */
Pubrel* pubrel = (Pubrel*)pack;
char *key = malloc(MESSAGE_FILENAME_LENGTH + 1);
if (!key)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
pubrel->MQTTVersion = c->MQTTVersion;
if (pubrel->MQTTVersion >= MQTTVERSION_5)
sprintf(key, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, pubrel->msgId);
else
sprintf(key, "%s%d", PERSISTENCE_PUBLISH_SENT, pubrel->msgId);
if (c->persistence->pcontainskey(c->phandle, key) != 0)
rc = c->persistence->premove(c->phandle, msgkeys[i]);
free(pubrel);
free(key);
}
}
else /* pack == NULL -> bad persisted record */
rc = c->persistence->premove(c->phandle, msgkeys[i]);
}
if (buffer)
{
free(buffer);
buffer = NULL;
}
if (msgkeys[i])
free(msgkeys[i]);
i++;
}
if (msgkeys)
free(msgkeys);
}
Log(TRACE_MINIMUM, -1, "%d sent messages and %d received messages restored for client %s\n",
msgs_sent, msgs_rcvd, c->clientID);
MQTTPersistence_wrapMsgID(c);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Returns a MQTT packet restored from persisted data.
* @param buffer the persisted data.
* @param buflen the number of bytes of the data buffer.
*/
void* MQTTPersistence_restorePacket(int MQTTVersion, char* buffer, size_t buflen)
{
void* pack = NULL;
Header header;
int fixed_header_length = 1, ptype, remaining_length = 0;
char c;
int multiplier = 1;
extern pf new_packets[];
FUNC_ENTRY;
header.byte = buffer[0];
/* decode the message length according to the MQTT algorithm */
do
{
c = *(++buffer);
remaining_length += (c & 127) * multiplier;
multiplier *= 128;
fixed_header_length++;
} while ((c & 128) != 0);
if ( (fixed_header_length + remaining_length) == buflen )
{
ptype = header.bits.type;
if (ptype >= CONNECT && ptype <= DISCONNECT && new_packets[ptype] != NULL)
pack = (*new_packets[ptype])(MQTTVersion, header.byte, ++buffer, remaining_length);
}
FUNC_EXIT;
return pack;
}
/**
* Inserts the specified message into the list, maintaining message ID order.
* @param list the list to insert the message into.
* @param content the message to add.
* @param size size of the message.
*/
void MQTTPersistence_insertInOrder(List* list, void* content, size_t size)
{
ListElement* index = NULL;
ListElement* current = NULL;
FUNC_ENTRY;
while(ListNextElement(list, &current) != NULL && index == NULL)
{
if ( ((Messages*)content)->msgid < ((Messages*)current->content)->msgid )
index = current;
}
ListInsert(list, content, size, index);
FUNC_EXIT;
}
/**
* Adds a record to the persistent store. This function must not be called for QoS0
* messages.
* @param socket the socket of the client.
* @param buf0 fixed header.
* @param buf0len length of the fixed header.
* @param count number of buffers representing the variable header and/or the payload.
* @param buffers the buffers representing the variable header and/or the payload.
* @param buflens length of the buffers representing the variable header and/or the payload.
* @param htype MQTT packet type - PUBLISH or PUBREL
* @param msgId the message ID.
* @param scr 0 indicates message in the sending direction; 1 indicates message in the
* receiving direction.
* @param the MQTT version being used (>= MQTTVERSION_5 means properties included)
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
*/
int MQTTPersistence_put(int socket, char* buf0, size_t buf0len, int count,
char** buffers, size_t* buflens, int htype, int msgId, int scr, int MQTTVersion)
{
int rc = 0;
extern ClientStates* bstate;
int nbufs, i;
int* lens = NULL;
char** bufs = NULL;
char *key;
Clients* client = NULL;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &socket, clientSocketCompare)->content);
if (client->persistence != NULL)
{
if ((key = malloc(MESSAGE_FILENAME_LENGTH + 1)) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
nbufs = 1 + count;
if ((lens = (int *)malloc(nbufs * sizeof(int))) == NULL)
{
free(key);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
if ((bufs = (char **)malloc(nbufs * sizeof(char *))) == NULL)
{
free(key);
free(lens);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
lens[0] = (int)buf0len;
bufs[0] = buf0;
for (i = 0; i < count; i++)
{
lens[i+1] = (int)buflens[i];
bufs[i+1] = buffers[i];
}
/* key */
if (scr == 0)
{ /* sending */
char* key_id = PERSISTENCE_PUBLISH_SENT;
if (htype == PUBLISH) /* PUBLISH QoS1 and QoS2*/
{
if (MQTTVersion >= MQTTVERSION_5)
key_id = PERSISTENCE_V5_PUBLISH_SENT;
}
else if (htype == PUBREL) /* PUBREL */
{
if (MQTTVersion >= MQTTVERSION_5)
key_id = PERSISTENCE_V5_PUBREL;
else
key_id = PERSISTENCE_PUBREL;
}
sprintf(key, "%s%d", key_id, msgId);
}
else if (scr == 1) /* receiving PUBLISH QoS2 */
{
char* key_id = PERSISTENCE_PUBLISH_RECEIVED;
if (MQTTVersion >= MQTTVERSION_5)
key_id = PERSISTENCE_V5_PUBLISH_RECEIVED;
sprintf(key, "%s%d", key_id, msgId);
}
rc = client->persistence->pput(client->phandle, key, nbufs, bufs, lens);
free(key);
free(lens);
free(bufs);
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Deletes a record from the persistent store.
* @param client the client as ::Clients.
* @param type the type of the persisted record: #PERSISTENCE_PUBLISH_SENT, #PERSISTENCE_PUBREL
* or #PERSISTENCE_PUBLISH_RECEIVED.
* @param qos the qos field of the message.
* @param msgId the message ID.
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
*/
int MQTTPersistence_remove(Clients* c, char *type, int qos, int msgId)
{
int rc = 0;
FUNC_ENTRY;
if (c->persistence != NULL)
{
char *key = malloc(MESSAGE_FILENAME_LENGTH + 1);
if (!key)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
if (strcmp(type, PERSISTENCE_PUBLISH_SENT) == 0 ||
strcmp(type, PERSISTENCE_V5_PUBLISH_SENT) == 0)
{
sprintf(key, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, msgId) ;
rc = c->persistence->premove(c->phandle, key);
sprintf(key, "%s%d", PERSISTENCE_V5_PUBREL, msgId) ;
rc += c->persistence->premove(c->phandle, key);
sprintf(key, "%s%d", PERSISTENCE_PUBLISH_SENT, msgId) ;
rc += c->persistence->premove(c->phandle, key);
sprintf(key, "%s%d", PERSISTENCE_PUBREL, msgId) ;
rc += c->persistence->premove(c->phandle, key);
}
else /* PERSISTENCE_PUBLISH_SENT && qos == 1 */
{ /* or PERSISTENCE_PUBLISH_RECEIVED */
sprintf(key, "%s%d", PERSISTENCE_V5_PUBLISH_RECEIVED, msgId);
rc = c->persistence->premove(c->phandle, key);
sprintf(key, "%s%d", PERSISTENCE_PUBLISH_RECEIVED, msgId);
rc += c->persistence->premove(c->phandle, key);
}
free(key);
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Checks whether the message IDs wrapped by looking for the largest gap between two consecutive
* message IDs in the outboundMsgs queue.
* @param client the client as ::Clients.
*/
void MQTTPersistence_wrapMsgID(Clients *client)
{
ListElement* wrapel = NULL;
ListElement* current = NULL;
FUNC_ENTRY;
if ( client->outboundMsgs->count > 0 )
{
int firstMsgID = ((Messages*)client->outboundMsgs->first->content)->msgid;
int lastMsgID = ((Messages*)client->outboundMsgs->last->content)->msgid;
int gap = MAX_MSG_ID - lastMsgID + firstMsgID;
current = ListNextElement(client->outboundMsgs, &current);
while(ListNextElement(client->outboundMsgs, &current) != NULL)
{
int curMsgID = ((Messages*)current->content)->msgid;
int curPrevMsgID = ((Messages*)current->prev->content)->msgid;
int curgap = curMsgID - curPrevMsgID;
if ( curgap > gap )
{
gap = curgap;
wrapel = current;
}
}
}
if ( wrapel != NULL )
{
/* put wrapel at the beginning of the queue */
client->outboundMsgs->first->prev = client->outboundMsgs->last;
client->outboundMsgs->last->next = client->outboundMsgs->first;
client->outboundMsgs->first = wrapel;
client->outboundMsgs->last = wrapel->prev;
client->outboundMsgs->first->prev = NULL;
client->outboundMsgs->last->next = NULL;
}
FUNC_EXIT;
}
#if !defined(NO_PERSISTENCE)
int MQTTPersistence_unpersistQueueEntry(Clients* client, MQTTPersistence_qEntry* qe)
{
int rc = 0;
char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
FUNC_ENTRY;
if (client->MQTTVersion >= MQTTVERSION_5)
sprintf(key, "%s%u", PERSISTENCE_V5_QUEUE_KEY, qe->seqno);
else
sprintf(key, "%s%u", PERSISTENCE_QUEUE_KEY, qe->seqno);
if ((rc = client->persistence->premove(client->phandle, key)) != 0)
Log(LOG_ERROR, 0, "Error %d removing qEntry from persistence", rc);
FUNC_EXIT_RC(rc);
return rc;
}
#define MAX_NO_OF_BUFFERS 9
int MQTTPersistence_persistQueueEntry(Clients* aclient, MQTTPersistence_qEntry* qe)
{
int rc = 0;
int bufindex = 0;
char key[PERSISTENCE_MAX_KEY_LENGTH + 1];
int lens[MAX_NO_OF_BUFFERS];
void* bufs[MAX_NO_OF_BUFFERS];
int props_allocated = 0;
FUNC_ENTRY;
bufs[bufindex] = &qe->msg->payloadlen;
lens[bufindex++] = sizeof(qe->msg->payloadlen);
bufs[bufindex] = qe->msg->payload;
lens[bufindex++] = qe->msg->payloadlen;
bufs[bufindex] = &qe->msg->qos;
lens[bufindex++] = sizeof(qe->msg->qos);
bufs[bufindex] = &qe->msg->retained;
lens[bufindex++] = sizeof(qe->msg->retained);
bufs[bufindex] = &qe->msg->dup;
lens[bufindex++] = sizeof(qe->msg->dup);
bufs[bufindex] = &qe->msg->msgid;
lens[bufindex++] = sizeof(qe->msg->msgid);
bufs[bufindex] = qe->topicName;
lens[bufindex++] = (int)strlen(qe->topicName) + 1;
bufs[bufindex] = &qe->topicLen;
lens[bufindex++] = sizeof(qe->topicLen);
if (aclient->MQTTVersion >= MQTTVERSION_5) /* persist properties */
{
MQTTProperties no_props = MQTTProperties_initializer;
MQTTProperties* props = &no_props;
int temp_len = 0;
char* ptr = NULL;
if (qe->msg->struct_version >= 1)
props = &qe->msg->properties;
temp_len = MQTTProperties_len(props);
ptr = bufs[bufindex] = malloc(temp_len);
if (!ptr)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
props_allocated = bufindex;
rc = MQTTProperties_write(&ptr, props);
lens[bufindex++] = temp_len;
sprintf(key, "%s%u", PERSISTENCE_V5_QUEUE_KEY, ++aclient->qentry_seqno);
}
else
sprintf(key, "%s%u", PERSISTENCE_QUEUE_KEY, ++aclient->qentry_seqno);
qe->seqno = aclient->qentry_seqno;
if ((rc = aclient->persistence->pput(aclient->phandle, key, bufindex, (char**)bufs, lens)) != 0)
Log(LOG_ERROR, 0, "Error persisting queue entry, rc %d", rc);
if (props_allocated != 0)
free(bufs[props_allocated]);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen, int MQTTVersion)
{
MQTTPersistence_qEntry* qe = NULL;
char* ptr = buffer;
int data_size;
FUNC_ENTRY;
if ((qe = malloc(sizeof(MQTTPersistence_qEntry))) == NULL)
goto exit;
memset(qe, '\0', sizeof(MQTTPersistence_qEntry));
if ((qe->msg = malloc(sizeof(MQTTPersistence_message))) == NULL)
{
free(qe);
qe = NULL;
goto exit;
}
memset(qe->msg, '\0', sizeof(MQTTPersistence_message));
qe->msg->struct_version = 1;
qe->msg->payloadlen = *(int*)ptr;
ptr += sizeof(int);
data_size = qe->msg->payloadlen;
if ((qe->msg->payload = malloc(data_size)) == NULL)
{
free(qe->msg);
free(qe);
qe = NULL;
goto exit;
}
memcpy(qe->msg->payload, ptr, data_size);
ptr += data_size;
qe->msg->qos = *(int*)ptr;
ptr += sizeof(int);
qe->msg->retained = *(int*)ptr;
ptr += sizeof(int);
qe->msg->dup = *(int*)ptr;
ptr += sizeof(int);
qe->msg->msgid = *(int*)ptr;
ptr += sizeof(int);
data_size = (int)strlen(ptr) + 1;
if ((qe->topicName = malloc(data_size)) == NULL)
{
free(qe->msg->payload);
free(qe->msg);
free(qe);
qe = NULL;
goto exit;
}
strcpy(qe->topicName, ptr);
ptr += data_size;
qe->topicLen = *(int*)ptr;
ptr += sizeof(int);
if (MQTTVersion >= MQTTVERSION_5 &&
MQTTProperties_read(&qe->msg->properties, &ptr, buffer + buflen) != 1)
Log(LOG_ERROR, -1, "Error restoring properties from persistence");
exit:
FUNC_EXIT;
return qe;
}
static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size)
{
ListElement* index = NULL;
ListElement* current = NULL;
FUNC_ENTRY;
while (ListNextElement(list, &current) != NULL && index == NULL)
{
if (qEntry->seqno < ((MQTTPersistence_qEntry*)current->content)->seqno)
index = current;
}
ListInsert(list, qEntry, size, index);
FUNC_EXIT;
}
/**
* Restores a queue of messages from persistence to memory
* @param c the client as ::Clients - the client object to restore the messages to
* @return return code, 0 if successful
*/
int MQTTPersistence_restoreMessageQueue(Clients* c)
{
int rc = 0;
char **msgkeys;
int nkeys;
int i = 0;
int entries_restored = 0;
FUNC_ENTRY;
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
{
while (rc == 0 && i < nkeys)
{
char *buffer = NULL;
int buflen;
if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) != 0 &&
strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) != 0)
{
; /* ignore if not a queue entry key */
}
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0)
{
int MQTTVersion =
(strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) == 0)
? MQTTVERSION_5 : MQTTVERSION_3_1_1;
MQTTPersistence_qEntry* qe = MQTTPersistence_restoreQueueEntry(buffer, buflen, MQTTVersion);
if (qe)
{
qe->seqno = atoi(strchr(msgkeys[i], '-')+1); /* key format is tag'-'seqno */
MQTTPersistence_insertInSeqOrder(c->messageQueue, qe, sizeof(MQTTPersistence_qEntry));
free(buffer);
c->qentry_seqno = max(c->qentry_seqno, qe->seqno);
entries_restored++;
}
}
if (msgkeys[i])
{
free(msgkeys[i]);
}
i++;
}
if (msgkeys != NULL)
free(msgkeys);
}
Log(TRACE_MINIMUM, -1, "%d queued messages restored for client %s", entries_restored, c->clientID);
FUNC_EXIT_RC(rc);
return rc;
}
#endif

View File

@ -0,0 +1,950 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - async client updates
* Ian Craggs - fix for bug 484496
* Ian Craggs - fix for issue 285
*******************************************************************************/
/**
* @file
* \brief A file system based persistence implementation.
*
* A directory is specified when the MQTT client is created. When the persistence is then
* opened (see ::Persistence_open), a sub-directory is made beneath the base for this
* particular client ID and connection key. This allows one persistence base directory to
* be shared by multiple clients.
*
*/
#if !defined(NO_PERSISTENCE)
#include "OsWrapper.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#if defined(_WIN32) || defined(_WIN64)
#include <direct.h>
/* Windows doesn't have strtok_r, so remap it to strtok */
#define strtok_r( A, B, C ) strtok( A, B )
int keysWin32(char *, char ***, int *);
int clearWin32(char *);
int containskeyWin32(char *, char *);
#else
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
int keysUnix(char *, char ***, int *);
int clearUnix(char *);
int containskeyUnix(char *, char *);
#endif
#include "MQTTClientPersistence.h"
#include "MQTTPersistenceDefault.h"
#include "StackTrace.h"
#include "Heap.h"
/** Create persistence directory for the client: context/clientID-serverURI.
* See ::Persistence_open
*/
int pstopen(void **handle, const char* clientID, const char* serverURI, void* context)
{
int rc = 0;
char *dataDir = context;
char *clientDir;
char *pToken = NULL;
char *save_ptr = NULL;
char *pCrtDirName = NULL;
char *pTokDirName = NULL;
char *perserverURI = NULL, *ptraux;
FUNC_ENTRY;
/* Note that serverURI=address:port, but ":" not allowed in Windows directories */
if ((perserverURI = malloc(strlen(serverURI) + 1)) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy(perserverURI, serverURI);
while ((ptraux = strstr(perserverURI, ":")) != NULL)
*ptraux = '-' ;
/* consider '/' + '-' + '\0' */
clientDir = malloc(strlen(dataDir) + strlen(clientID) + strlen(perserverURI) + 3);
if (!clientDir)
{
free(perserverURI);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(clientDir, "%s/%s-%s", dataDir, clientID, perserverURI);
/* create clientDir directory */
/* pCrtDirName - holds the directory name we are currently trying to create. */
/* This gets built up level by level untipwdl the full path name is created.*/
/* pTokDirName - holds the directory name that gets used by strtok. */
if ((pCrtDirName = (char*)malloc(strlen(clientDir) + 1)) == NULL)
{
free(clientDir);
free(perserverURI);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
if ((pTokDirName = (char*)malloc( strlen(clientDir) + 1 )) == NULL)
{
free(pCrtDirName);
free(clientDir);
free(perserverURI);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy( pTokDirName, clientDir );
/* If first character is directory separator, make sure it's in the created directory name #285 */
if (*pTokDirName == '/' || *pTokDirName == '\\')
{
*pCrtDirName = *pTokDirName;
pToken = strtok_r( pTokDirName + 1, "\\/", &save_ptr );
strcpy( pCrtDirName + 1, pToken );
}
else
{
pToken = strtok_r( pTokDirName, "\\/", &save_ptr );
strcpy( pCrtDirName, pToken );
}
rc = pstmkdir( pCrtDirName );
pToken = strtok_r( NULL, "\\/", &save_ptr );
while ( (pToken != NULL) && (rc == 0) )
{
/* Append the next directory level and try to create it */
strcat( pCrtDirName, "/" );
strcat( pCrtDirName, pToken );
rc = pstmkdir( pCrtDirName );
pToken = strtok_r( NULL, "\\/", &save_ptr );
}
*handle = clientDir;
free(pTokDirName);
free(pCrtDirName);
free(perserverURI);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/** Function to create a directory.
* Returns 0 on success or if the directory already exists.
*/
int pstmkdir( char *pPathname )
{
int rc = 0;
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
if ( _mkdir( pPathname ) != 0 )
{
#else
/* Create a directory with read, write and execute access for the owner and read access for the group */
#if !defined(_WRS_KERNEL)
if ( mkdir( pPathname, S_IRWXU | S_IRGRP ) != 0 )
#else
if ( mkdir( pPathname ) != 0 )
#endif /* !defined(_WRS_KERNEL) */
{
#endif
if ( errno != EEXIST )
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
FUNC_EXIT_RC(rc);
return rc;
}
/** Write wire message to the client persistence directory.
* See ::Persistence_put
*/
int pstput(void* handle, char* key, int bufcount, char* buffers[], int buflens[])
{
int rc = 0;
char *clientDir = handle;
char *file;
FILE *fp;
size_t bytesWritten = 0,
bytesTotal = 0;
int i;
FUNC_ENTRY;
if (clientDir == NULL)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
/* consider '/' + '\0' */
file = malloc(strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2 );
if (!file)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(file, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION);
fp = fopen(file, "wb");
if ( fp != NULL )
{
for(i=0; i<bufcount; i++)
{
bytesTotal += buflens[i];
bytesWritten += fwrite(buffers[i], sizeof(char), buflens[i], fp );
}
fclose(fp);
fp = NULL;
} else
rc = MQTTCLIENT_PERSISTENCE_ERROR;
if (bytesWritten != bytesTotal)
{
pstremove(handle, key);
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
free(file);
exit:
FUNC_EXIT_RC(rc);
return rc;
};
/** Retrieve a wire message from the client persistence directory.
* See ::Persistence_get
*/
int pstget(void* handle, char* key, char** buffer, int* buflen)
{
int rc = 0;
FILE *fp = NULL;
char *clientDir = handle;
char *file = NULL;
char *buf = NULL;
unsigned long fileLen = 0;
unsigned long bytesRead = 0;
FUNC_ENTRY;
if (clientDir == NULL)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
/* consider '/' + '\0' */
file = malloc(strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2);
if (!file)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(file, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION);
fp = fopen(file, "rb");
if ( fp != NULL )
{
fseek(fp, 0, SEEK_END);
fileLen = ftell(fp);
fseek(fp, 0, SEEK_SET);
if ((buf = (char *)malloc(fileLen)) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
bytesRead = (int)fread(buf, sizeof(char), fileLen, fp);
*buffer = buf;
*buflen = bytesRead;
if ( bytesRead != fileLen )
rc = MQTTCLIENT_PERSISTENCE_ERROR;
} else
rc = MQTTCLIENT_PERSISTENCE_ERROR;
/* the caller must free buf */
exit:
if (file)
free(file);
if (fp)
fclose(fp);
FUNC_EXIT_RC(rc);
return rc;
}
/** Delete a persisted message from the client persistence directory.
* See ::Persistence_remove
*/
int pstremove(void* handle, char* key)
{
int rc = 0;
char *clientDir = handle;
char *file;
FUNC_ENTRY;
if (clientDir == NULL)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
/* consider '/' + '\0' */
file = malloc(strlen(clientDir) + strlen(key) + strlen(MESSAGE_FILENAME_EXTENSION) + 2);
if (!file)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(file, "%s/%s%s", clientDir, key, MESSAGE_FILENAME_EXTENSION);
#if defined(_WIN32) || defined(_WIN64)
if ( _unlink(file) != 0 )
{
#else
if ( unlink(file) != 0 )
{
#endif
if ( errno != ENOENT )
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
free(file);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/** Delete client persistence directory (if empty).
* See ::Persistence_close
*/
int pstclose(void* handle)
{
int rc = 0;
char *clientDir = handle;
FUNC_ENTRY;
if (clientDir == NULL)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
#if defined(_WIN32) || defined(_WIN64)
if ( _rmdir(clientDir) != 0 )
{
#else
if ( rmdir(clientDir) != 0 )
{
#endif
if ( errno != ENOENT && errno != ENOTEMPTY )
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
free(clientDir);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/** Returns whether if a wire message is persisted in the client persistence directory.
* See ::Persistence_containskey
*/
int pstcontainskey(void *handle, char *key)
{
int rc = 0;
char *clientDir = handle;
FUNC_ENTRY;
if (clientDir == NULL)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
#if defined(_WIN32) || defined(_WIN64)
rc = containskeyWin32(clientDir, key);
#else
rc = containskeyUnix(clientDir, key);
#endif
exit:
FUNC_EXIT_RC(rc);
return rc;
}
#if defined(_WIN32) || defined(_WIN64)
int containskeyWin32(char *dirname, char *key)
{
int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
int fFinished = 0;
char *filekey, *ptraux;
char dir[MAX_PATH+1];
WIN32_FIND_DATAA FileData;
HANDLE hDir;
FUNC_ENTRY;
sprintf(dir, "%s/*", dirname);
hDir = FindFirstFileA(dir, &FileData);
if (hDir != INVALID_HANDLE_VALUE)
{
while (!fFinished)
{
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
{
if ((filekey = malloc(strlen(FileData.cFileName) + 1)) == NULL)
{
notFound = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy(filekey, FileData.cFileName);
ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
if ( ptraux != NULL )
*ptraux = '\0' ;
if(strcmp(filekey, key) == 0)
{
notFound = 0;
fFinished = 1;
}
free(filekey);
}
if (!FindNextFileA(hDir, &FileData))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
fFinished = 1;
}
}
FindClose(hDir);
}
exit:
FUNC_EXIT_RC(notFound);
return notFound;
}
#else
int containskeyUnix(char *dirname, char *key)
{
int notFound = MQTTCLIENT_PERSISTENCE_ERROR;
char *filekey, *ptraux;
DIR *dp = NULL;
struct dirent *dir_entry;
struct stat stat_info;
FUNC_ENTRY;
if((dp = opendir(dirname)) != NULL)
{
while((dir_entry = readdir(dp)) != NULL && notFound)
{
char* filename = malloc(strlen(dirname) + strlen(dir_entry->d_name) + 2);
if (!filename)
{
notFound = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(filename, "%s/%s", dirname, dir_entry->d_name);
lstat(filename, &stat_info);
free(filename);
if(S_ISREG(stat_info.st_mode))
{
if ((filekey = malloc(strlen(dir_entry->d_name) + 1)) == NULL)
{
notFound = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy(filekey, dir_entry->d_name);
ptraux = strstr(filekey, MESSAGE_FILENAME_EXTENSION);
if ( ptraux != NULL )
*ptraux = '\0' ;
if(strcmp(filekey, key) == 0)
notFound = 0;
free(filekey);
}
}
}
exit:
if (dp)
closedir(dp);
FUNC_EXIT_RC(notFound);
return notFound;
}
#endif
/** Delete all the persisted message in the client persistence directory.
* See ::Persistence_clear
*/
int pstclear(void *handle)
{
int rc = 0;
char *clientDir = handle;
FUNC_ENTRY;
if (clientDir == NULL)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
#if defined(_WIN32) || defined(_WIN64)
rc = clearWin32(clientDir);
#else
rc = clearUnix(clientDir);
#endif
exit:
FUNC_EXIT_RC(rc);
return rc;
}
#if defined(_WIN32) || defined(_WIN64)
int clearWin32(char *dirname)
{
int rc = 0;
int fFinished = 0;
char *file;
char dir[MAX_PATH+1];
WIN32_FIND_DATAA FileData;
HANDLE hDir;
FUNC_ENTRY;
sprintf(dir, "%s/*", dirname);
hDir = FindFirstFileA(dir, &FileData);
if (hDir != INVALID_HANDLE_VALUE)
{
while (!fFinished)
{
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
{
file = malloc(strlen(dirname) + strlen(FileData.cFileName) + 2);
if (!file)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(file, "%s/%s", dirname, FileData.cFileName);
rc = remove(file);
free(file);
if ( rc != 0 )
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
break;
}
}
if (!FindNextFileA(hDir, &FileData))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
fFinished = 1;
}
}
FindClose(hDir);
} else
rc = MQTTCLIENT_PERSISTENCE_ERROR;
exit:
FUNC_EXIT_RC(rc);
return rc;
}
#else
int clearUnix(char *dirname)
{
int rc = 0;
DIR *dp;
struct dirent *dir_entry;
struct stat stat_info;
FUNC_ENTRY;
if((dp = opendir(dirname)) != NULL)
{
while((dir_entry = readdir(dp)) != NULL && rc == 0)
{
lstat(dir_entry->d_name, &stat_info);
if(S_ISREG(stat_info.st_mode))
{
if (remove(dir_entry->d_name) != 0 && errno != ENOENT)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
}
closedir(dp);
} else
rc = MQTTCLIENT_PERSISTENCE_ERROR;
FUNC_EXIT_RC(rc);
return rc;
}
#endif
/** Returns the keys (file names w/o the extension) in the client persistence directory.
* See ::Persistence_keys
*/
int pstkeys(void *handle, char ***keys, int *nkeys)
{
int rc = 0;
char *clientDir = handle;
FUNC_ENTRY;
if (clientDir == NULL)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
#if defined(_WIN32) || defined(_WIN64)
rc = keysWin32(clientDir, keys, nkeys);
#else
rc = keysUnix(clientDir, keys, nkeys);
#endif
exit:
FUNC_EXIT_RC(rc);
return rc;
}
#if defined(_WIN32) || defined(_WIN64)
int keysWin32(char *dirname, char ***keys, int *nkeys)
{
int rc = 0;
char **fkeys = NULL;
int nfkeys = 0;
char dir[MAX_PATH+1];
WIN32_FIND_DATAA FileData;
HANDLE hDir;
int fFinished = 0;
char *ptraux;
int i;
FUNC_ENTRY;
sprintf(dir, "%s/*", dirname);
/* get number of keys */
hDir = FindFirstFileA(dir, &FileData);
if (hDir != INVALID_HANDLE_VALUE)
{
while (!fFinished)
{
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
nfkeys++;
if (!FindNextFileA(hDir, &FileData))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
fFinished = 1;
}
}
FindClose(hDir);
} else
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
if (nfkeys != 0)
{
if ((fkeys = (char **)malloc(nfkeys * sizeof(char *))) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
}
/* copy the keys */
hDir = FindFirstFileA(dir, &FileData);
if (hDir != INVALID_HANDLE_VALUE)
{
fFinished = 0;
i = 0;
while (!fFinished)
{
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
{
if ((fkeys[i] = malloc(strlen(FileData.cFileName) + 1)) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy(fkeys[i], FileData.cFileName);
ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
if ( ptraux != NULL )
*ptraux = '\0' ;
i++;
}
if (!FindNextFileA(hDir, &FileData))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
fFinished = 1;
}
}
FindClose(hDir);
} else
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
*nkeys = nfkeys;
*keys = fkeys;
/* the caller must free keys */
exit:
FUNC_EXIT_RC(rc);
return rc;
}
#else
int keysUnix(char *dirname, char ***keys, int *nkeys)
{
int rc = 0;
char **fkeys = NULL;
int nfkeys = 0;
char *ptraux;
int i;
DIR *dp = NULL;
struct dirent *dir_entry;
struct stat stat_info;
FUNC_ENTRY;
/* get number of keys */
if((dp = opendir(dirname)) != NULL)
{
while((dir_entry = readdir(dp)) != NULL)
{
char* temp = malloc(strlen(dirname)+strlen(dir_entry->d_name)+2);
if (!temp)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(temp, "%s/%s", dirname, dir_entry->d_name);
if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
nfkeys++;
free(temp);
}
closedir(dp);
dp = NULL;
} else
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
if (nfkeys != 0)
{
if ((fkeys = (char **)malloc(nfkeys * sizeof(char *))) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
/* copy the keys */
if((dp = opendir(dirname)) != NULL)
{
i = 0;
while((dir_entry = readdir(dp)) != NULL)
{
char* temp = malloc(strlen(dirname)+strlen(dir_entry->d_name)+2);
if (!temp)
{
free(fkeys);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
sprintf(temp, "%s/%s", dirname, dir_entry->d_name);
if (lstat(temp, &stat_info) == 0 && S_ISREG(stat_info.st_mode))
{
if ((fkeys[i] = malloc(strlen(dir_entry->d_name) + 1)) == NULL)
{
free(temp);
free(fkeys);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy(fkeys[i], dir_entry->d_name);
ptraux = strstr(fkeys[i], MESSAGE_FILENAME_EXTENSION);
if ( ptraux != NULL )
*ptraux = '\0' ;
i++;
}
free(temp);
}
} else
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
goto exit;
}
}
*nkeys = nfkeys;
*keys = fkeys;
/* the caller must free keys */
exit:
if (dp)
closedir(dp);
FUNC_EXIT_RC(rc);
return rc;
}
#endif
#if defined(UNIT_TESTS)
int main (int argc, char *argv[])
{
#define MSTEM "m-"
#define NMSGS 10
#define NBUFS 4
#define NDEL 2
#define RC !rc ? "(Success)" : "(Failed) "
int rc;
char *handle;
char *perdir = ".";
const char *clientID = "TheUTClient";
const char *serverURI = "127.0.0.1:1883";
char *stem = MSTEM;
int msgId, i;
int nm[NDEL] = {5 , 8}; /* msgIds to get and remove */
char *key;
char **keys;
int nkeys;
char *buffer, *buff;
int buflen;
int nbufs = NBUFS;
char *bufs[NBUFS] = {"m0", "mm1", "mmm2" , "mmmm3"}; /* message content */
int buflens[NBUFS];
for(i=0;i<nbufs;i++)
buflens[i]=strlen(bufs[i]);
/* open */
/* printf("Persistence directory : %s\n", perdir); */
rc = pstopen((void**)&handle, clientID, serverURI, perdir);
printf("%s Persistence directory for client %s : %s\n", RC, clientID, handle);
/* put */
for(msgId=0;msgId<NMSGS;msgId++)
{
key = malloc(MESSAGE_FILENAME_LENGTH + 1);
sprintf(key, "%s%d", stem, msgId);
rc = pstput(handle, key, nbufs, bufs, buflens);
printf("%s Adding message %s\n", RC, key);
free(key);
}
/* keys ,ie, list keys added */
rc = pstkeys(handle, &keys, &nkeys);
printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
for(i=0;i<nkeys;i++)
printf("%13s\n", keys[i]);
if (keys !=NULL)
free(keys);
/* containskey */
for(i=0;i<NDEL;i++)
{
key = malloc(MESSAGE_FILENAME_LENGTH + 1);
sprintf(key, "%s%d", stem, nm[i]);
rc = pstcontainskey(handle, key);
printf("%s Message %s is persisted ?\n", RC, key);
free(key);
}
/* get && remove*/
for(i=0;i<NDEL;i++)
{
key = malloc(MESSAGE_FILENAME_LENGTH + 1);
sprintf(key, "%s%d", stem, nm[i]);
rc = pstget(handle, key, &buffer, &buflen);
buff = malloc(buflen+1);
memcpy(buff, buffer, buflen);
buff[buflen] = '\0';
printf("%s Retrieving message %s : %s\n", RC, key, buff);
rc = pstremove(handle, key);
printf("%s Removing message %s\n", RC, key);
free(key);
free(buff);
free(buffer);
}
/* containskey */
for(i=0;i<NDEL;i++)
{
key = malloc(MESSAGE_FILENAME_LENGTH + 1);
sprintf(key, "%s%d", stem, nm[i]);
rc = pstcontainskey(handle, key);
printf("%s Message %s is persisted ?\n", RC, key);
free(key);
}
/* keys ,ie, list keys added */
rc = pstkeys(handle, &keys, &nkeys);
printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
for(i=0;i<nkeys;i++)
printf("%13s\n", keys[i]);
if (keys != NULL)
free(keys);
/* close -> it will fail, since client persistence directory is not empty */
rc = pstclose(&handle);
printf("%s Closing client persistence directory for client %s\n", RC, clientID);
/* clear */
rc = pstclear(handle);
printf("%s Deleting all persisted messages in %s\n", RC, handle);
/* keys ,ie, list keys added */
rc = pstkeys(handle, &keys, &nkeys);
printf("%s Found %d messages persisted in %s\n", RC, nkeys, handle);
for(i=0;i<nkeys;i++)
printf("%13s\n", keys[i]);
if ( keys != NULL )
free(keys);
/* close */
rc = pstclose(&handle);
printf("%s Closing client persistence directory for client %s\n", RC, clientID);
}
#endif
#endif /* NO_PERSISTENCE */

533
deps/paho.mqtt.c/src/MQTTProperties.c vendored Normal file
View File

@ -0,0 +1,533 @@
/*******************************************************************************
* Copyright (c) 2017, 2020 IBM Corp. and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#include "MQTTProperties.h"
#include "MQTTPacket.h"
#include "MQTTProtocolClient.h"
#include "Heap.h"
#include "StackTrace.h"
#include <memory.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static struct nameToType
{
enum MQTTPropertyCodes name;
enum MQTTPropertyTypes type;
} namesToTypes[] =
{
{MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, MQTTPROPERTY_TYPE_BYTE},
{MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
{MQTTPROPERTY_CODE_CONTENT_TYPE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
{MQTTPROPERTY_CODE_RESPONSE_TOPIC, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
{MQTTPROPERTY_CODE_CORRELATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER},
{MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
{MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
{MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
{MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
{MQTTPROPERTY_CODE_AUTHENTICATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
{MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
{MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
{MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
{MQTTPROPERTY_CODE_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
{MQTTPROPERTY_CODE_SERVER_REFERENCE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
{MQTTPROPERTY_CODE_REASON_STRING, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
{MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
{MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
{MQTTPROPERTY_CODE_TOPIC_ALIAS, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
{MQTTPROPERTY_CODE_MAXIMUM_QOS, MQTTPROPERTY_TYPE_BYTE},
{MQTTPROPERTY_CODE_RETAIN_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
{MQTTPROPERTY_CODE_USER_PROPERTY, MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR},
{MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
{MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
{MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE}
};
static char* datadup(const MQTTLenString* str)
{
char* temp = malloc(str->len);
if (temp)
memcpy(temp, str->data, str->len);
return temp;
}
int MQTTProperty_getType(enum MQTTPropertyCodes value)
{
int i, rc = -1;
for (i = 0; i < ARRAY_SIZE(namesToTypes); ++i)
{
if (namesToTypes[i].name == value)
{
rc = namesToTypes[i].type;
break;
}
}
return rc;
}
int MQTTProperties_len(MQTTProperties* props)
{
/* properties length is an mbi */
return (props == NULL) ? 1 : props->length + MQTTPacket_VBIlen(props->length);
}
/**
* Add a new property to a property list
* @param props the property list
* @param prop the new property
* @return code 0 is success
*/
int MQTTProperties_add(MQTTProperties* props, const MQTTProperty* prop)
{
int rc = 0, type;
if ((type = MQTTProperty_getType(prop->identifier)) < 0)
{
/*StackTrace_printStack(stdout);*/
rc = MQTT_INVALID_PROPERTY_ID;
goto exit;
}
else if (props->array == NULL)
{
props->max_count = 10;
props->array = malloc(sizeof(MQTTProperty) * props->max_count);
}
else if (props->count == props->max_count)
{
props->max_count += 10;
props->array = realloc(props->array, sizeof(MQTTProperty) * props->max_count);
}
if (props->array)
{
int len = 0;
props->array[props->count++] = *prop;
/* calculate length */
switch (type)
{
case MQTTPROPERTY_TYPE_BYTE:
len = 1;
break;
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
len = 2;
break;
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
len = 4;
break;
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
len = MQTTPacket_VBIlen(prop->value.integer4);
break;
case MQTTPROPERTY_TYPE_BINARY_DATA:
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
len = 2 + prop->value.data.len;
props->array[props->count-1].value.data.data = datadup(&prop->value.data);
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
{
len += 2 + prop->value.value.len;
props->array[props->count-1].value.value.data = datadup(&prop->value.value);
}
break;
}
props->length += len + 1; /* add identifier byte */
}
else
rc = PAHO_MEMORY_ERROR;
exit:
return rc;
}
int MQTTProperty_write(char** pptr, MQTTProperty* prop)
{
int rc = -1,
type = -1;
type = MQTTProperty_getType(prop->identifier);
if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
{
writeChar(pptr, prop->identifier);
switch (type)
{
case MQTTPROPERTY_TYPE_BYTE:
writeChar(pptr, prop->value.byte);
rc = 1;
break;
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
writeInt(pptr, prop->value.integer2);
rc = 2;
break;
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
writeInt4(pptr, prop->value.integer4);
rc = 4;
break;
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
rc = MQTTPacket_encode(*pptr, prop->value.integer4);
*pptr += rc;
break;
case MQTTPROPERTY_TYPE_BINARY_DATA:
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
writeMQTTLenString(pptr, prop->value.data);
rc = prop->value.data.len + 2; /* include length field */
break;
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
writeMQTTLenString(pptr, prop->value.data);
writeMQTTLenString(pptr, prop->value.value);
rc = prop->value.data.len + prop->value.value.len + 4; /* include length fields */
break;
}
}
return rc + 1; /* include identifier byte */
}
int MQTTProperties_write(char** pptr, const MQTTProperties* properties)
{
int rc = -1;
int i = 0, len = 0;
/* write the entire property list length first */
if (properties == NULL)
{
*pptr += MQTTPacket_encode(*pptr, 0);
rc = 1;
}
else
{
*pptr += MQTTPacket_encode(*pptr, properties->length);
len = rc = 1;
for (i = 0; i < properties->count; ++i)
{
rc = MQTTProperty_write(pptr, &properties->array[i]);
if (rc < 0)
break;
else
len += rc;
}
if (rc >= 0)
rc = len;
}
return rc;
}
int MQTTProperty_read(MQTTProperty* prop, char** pptr, char* enddata)
{
int type = -1,
len = 0;
prop->identifier = readChar(pptr);
type = MQTTProperty_getType(prop->identifier);
if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
{
switch (type)
{
case MQTTPROPERTY_TYPE_BYTE:
prop->value.byte = readChar(pptr);
len = 1;
break;
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
prop->value.integer2 = readInt(pptr);
len = 2;
break;
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
prop->value.integer4 = readInt4(pptr);
len = 4;
break;
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
len = MQTTPacket_decodeBuf(*pptr, &prop->value.integer4);
*pptr += len;
break;
case MQTTPROPERTY_TYPE_BINARY_DATA:
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
len = MQTTLenStringRead(&prop->value.data, pptr, enddata);
prop->value.data.data = datadup(&prop->value.data);
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
{
len += MQTTLenStringRead(&prop->value.value, pptr, enddata);
prop->value.value.data = datadup(&prop->value.value);
}
break;
}
}
return len + 1; /* 1 byte for identifier */
}
int MQTTProperties_read(MQTTProperties* properties, char** pptr, char* enddata)
{
int rc = 0;
unsigned int remlength = 0;
FUNC_ENTRY;
/* we assume an initialized properties structure */
if (enddata - (*pptr) > 0) /* enough length to read the VBI? */
{
*pptr += MQTTPacket_decodeBuf(*pptr, &remlength);
properties->length = remlength;
while (remlength > 0)
{
if (properties->count == properties->max_count)
{
properties->max_count += 10;
if (properties->max_count == 10)
properties->array = malloc(sizeof(MQTTProperty) * properties->max_count);
else
properties->array = realloc(properties->array, sizeof(MQTTProperty) * properties->max_count);
}
if (properties->array == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
remlength -= MQTTProperty_read(&properties->array[properties->count], pptr, enddata);
properties->count++;
}
if (remlength == 0)
rc = 1; /* data read successfully */
}
if (rc != 1 && properties->array != NULL)
{
free(properties->array);
properties->array = NULL;
properties->max_count = properties->count = 0;
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}
struct {
enum MQTTPropertyCodes value;
const char* name;
} nameToString[] =
{
{MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, "PAYLOAD_FORMAT_INDICATOR"},
{MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, "MESSAGE_EXPIRY_INTERVAL"},
{MQTTPROPERTY_CODE_CONTENT_TYPE, "CONTENT_TYPE"},
{MQTTPROPERTY_CODE_RESPONSE_TOPIC, "RESPONSE_TOPIC"},
{MQTTPROPERTY_CODE_CORRELATION_DATA, "CORRELATION_DATA"},
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, "SUBSCRIPTION_IDENTIFIER"},
{MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, "SESSION_EXPIRY_INTERVAL"},
{MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER, "ASSIGNED_CLIENT_IDENTIFER"},
{MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, "SERVER_KEEP_ALIVE"},
{MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, "AUTHENTICATION_METHOD"},
{MQTTPROPERTY_CODE_AUTHENTICATION_DATA, "AUTHENTICATION_DATA"},
{MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, "REQUEST_PROBLEM_INFORMATION"},
{MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, "WILL_DELAY_INTERVAL"},
{MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, "REQUEST_RESPONSE_INFORMATION"},
{MQTTPROPERTY_CODE_RESPONSE_INFORMATION, "RESPONSE_INFORMATION"},
{MQTTPROPERTY_CODE_SERVER_REFERENCE, "SERVER_REFERENCE"},
{MQTTPROPERTY_CODE_REASON_STRING, "REASON_STRING"},
{MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, "RECEIVE_MAXIMUM"},
{MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, "TOPIC_ALIAS_MAXIMUM"},
{MQTTPROPERTY_CODE_TOPIC_ALIAS, "TOPIC_ALIAS"},
{MQTTPROPERTY_CODE_MAXIMUM_QOS, "MAXIMUM_QOS"},
{MQTTPROPERTY_CODE_RETAIN_AVAILABLE, "RETAIN_AVAILABLE"},
{MQTTPROPERTY_CODE_USER_PROPERTY, "USER_PROPERTY"},
{MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, "MAXIMUM_PACKET_SIZE"},
{MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, "WILDCARD_SUBSCRIPTION_AVAILABLE"},
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, "SUBSCRIPTION_IDENTIFIERS_AVAILABLE"},
{MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, "SHARED_SUBSCRIPTION_AVAILABLE"}
};
const char* MQTTPropertyName(enum MQTTPropertyCodes value)
{
int i = 0;
const char* result = NULL;
for (i = 0; i < ARRAY_SIZE(nameToString); ++i)
{
if (nameToString[i].value == value)
{
result = nameToString[i].name;
break;
}
}
return result;
}
void MQTTProperties_free(MQTTProperties* props)
{
int i = 0;
FUNC_ENTRY;
if (props == NULL)
goto exit;
for (i = 0; i < props->count; ++i)
{
int id = props->array[i].identifier;
int type = MQTTProperty_getType(id);
switch (type)
{
case MQTTPROPERTY_TYPE_BINARY_DATA:
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
free(props->array[i].value.data.data);
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
free(props->array[i].value.value.data);
break;
}
}
if (props->array)
free(props->array);
memset(props, '\0', sizeof(MQTTProperties)); /* zero all fields */
exit:
FUNC_EXIT;
}
MQTTProperties MQTTProperties_copy(const MQTTProperties* props)
{
int i = 0;
MQTTProperties result = MQTTProperties_initializer;
FUNC_ENTRY;
for (i = 0; i < props->count; ++i)
{
int rc = 0;
if ((rc = MQTTProperties_add(&result, &props->array[i])) != 0)
Log(LOG_ERROR, -1, "Error from MQTTProperties add %d", rc);
}
FUNC_EXIT;
return result;
}
int MQTTProperties_hasProperty(MQTTProperties *props, enum MQTTPropertyCodes propid)
{
int i = 0;
int found = 0;
for (i = 0; i < props->count; ++i)
{
if (propid == props->array[i].identifier)
{
found = 1;
break;
}
}
return found;
}
int MQTTProperties_propertyCount(MQTTProperties *props, enum MQTTPropertyCodes propid)
{
int i = 0;
int count = 0;
for (i = 0; i < props->count; ++i)
{
if (propid == props->array[i].identifier)
count++;
}
return count;
}
int MQTTProperties_getNumericValueAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
{
int i = 0;
int rc = -9999999;
int cur_index = 0;
for (i = 0; i < props->count; ++i)
{
int id = props->array[i].identifier;
if (id == propid)
{
if (cur_index < index)
{
cur_index++;
continue;
}
switch (MQTTProperty_getType(id))
{
case MQTTPROPERTY_TYPE_BYTE:
rc = props->array[i].value.byte;
break;
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
rc = props->array[i].value.integer2;
break;
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
rc = props->array[i].value.integer4;
break;
default:
rc = -999999;
break;
}
break;
}
}
return rc;
}
int MQTTProperties_getNumericValue(MQTTProperties *props, enum MQTTPropertyCodes propid)
{
return MQTTProperties_getNumericValueAt(props, propid, 0);
}
MQTTProperty* MQTTProperties_getPropertyAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
{
int i = 0;
MQTTProperty* result = NULL;
int cur_index = 0;
for (i = 0; i < props->count; ++i)
{
int id = props->array[i].identifier;
if (id == propid)
{
if (cur_index == index)
{
result = &props->array[i];
break;
}
else
cur_index++;
}
}
return result;
}
MQTTProperty* MQTTProperties_getProperty(MQTTProperties *props, enum MQTTPropertyCodes propid)
{
return MQTTProperties_getPropertyAt(props, propid, 0);
}

View File

@ -0,0 +1,926 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* 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 - fix for bug 413429 - connectionLost not called
* Ian Craggs - fix for bug 421103 - trying to write to same socket, in retry
* Rong Xiang, Ian Craggs - C++ compatibility
* Ian Craggs - turn off DUP flag for PUBREL - MQTT 3.1.1
* Ian Craggs - ensure that acks are not sent if write is outstanding on socket
* Ian Craggs - MQTT 5.0 support
*******************************************************************************/
/**
* @file
* \brief Functions dealing with the MQTT protocol exchanges
*
* Some other related functions are in the MQTTProtocolOut module
* */
#include <stdlib.h>
#include <string.h>
#include "MQTTProtocolClient.h"
#if !defined(NO_PERSISTENCE)
#include "MQTTPersistence.h"
#endif
#include "SocketBuffer.h"
#include "StackTrace.h"
#include "Heap.h"
#if !defined(min)
#define min(A,B) ( (A) < (B) ? (A):(B))
#endif
extern MQTTProtocol state;
extern ClientStates* bstate;
static void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish);
static int MQTTProtocol_startPublishCommon(
Clients* pubclient,
Publish* publish,
int qos,
int retained);
static void MQTTProtocol_retries(START_TIME_TYPE now, Clients* client, int regardless);
/**
* List callback function for comparing Message structures by message id
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int messageIDCompare(void* a, void* b)
{
Messages* msg = (Messages*)a;
return msg->msgid == *(int*)b;
}
/**
* Assign a new message id for a client. Make sure it isn't already being used and does
* not exceed the maximum.
* @param client a client structure
* @return the next message id to use, or 0 if none available
*/
int MQTTProtocol_assignMsgId(Clients* client)
{
int start_msgid = client->msgID;
int msgid = start_msgid;
FUNC_ENTRY;
msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
while (ListFindItem(client->outboundMsgs, &msgid, messageIDCompare) != NULL)
{
msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
if (msgid == start_msgid)
{ /* we've tried them all - none free */
msgid = 0;
break;
}
}
if (msgid != 0)
client->msgID = msgid;
FUNC_EXIT_RC(msgid);
return msgid;
}
static void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish)
{
int len;
pending_write* pw = NULL;
FUNC_ENTRY;
/* store the publication until the write is finished */
if ((pw = malloc(sizeof(pending_write))) == NULL)
goto exit;
Log(TRACE_MIN, 12, NULL);
if ((pw->p = MQTTProtocol_storePublication(publish, &len)) == NULL)
{
free(pw);
goto exit;
}
pw->socket = pubclient->net.socket;
if (!ListAppend(&(state.pending_writes), pw, sizeof(pending_write)+len))
{
free(pw->p);
free(pw);
goto exit;
}
/* we don't copy QoS 0 messages unless we have to, so now we have to tell the socket buffer where
the saved copy is */
if (SocketBuffer_updateWrite(pw->socket, pw->p->topic, pw->p->payload) == NULL)
Log(LOG_SEVERE, 0, "Error updating write");
exit:
FUNC_EXIT;
}
/**
* Utility function to start a new publish exchange.
* @param pubclient the client to send the publication to
* @param publish the publication data
* @param qos the MQTT QoS to use
* @param retained boolean - whether to set the MQTT retained flag
* @return the completion code
*/
static int MQTTProtocol_startPublishCommon(Clients* pubclient, Publish* publish, int qos, int retained)
{
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
rc = MQTTPacket_send_publish(publish, 0, qos, retained, &pubclient->net, pubclient->clientID);
if (qos == 0 && rc == TCPSOCKET_INTERRUPTED)
MQTTProtocol_storeQoS0(pubclient, publish);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Start a new publish exchange. Store any state necessary and try to send the packet
* @param pubclient the client to send the publication to
* @param publish the publication data
* @param qos the MQTT QoS to use
* @param retained boolean - whether to set the MQTT retained flag
* @param mm - pointer to the message to send
* @return the completion code
*/
int MQTTProtocol_startPublish(Clients* pubclient, Publish* publish, int qos, int retained, Messages** mm)
{
Publish p = *publish;
int rc = 0;
FUNC_ENTRY;
if (qos > 0)
{
*mm = MQTTProtocol_createMessage(publish, mm, qos, retained, 0);
ListAppend(pubclient->outboundMsgs, *mm, (*mm)->len);
/* we change these pointers to the saved message location just in case the packet could not be written
entirely; the socket buffer will use these locations to finish writing the packet */
p.payload = (*mm)->publish->payload;
p.topic = (*mm)->publish->topic;
p.properties = (*mm)->properties;
p.MQTTVersion = (*mm)->MQTTVersion;
}
rc = MQTTProtocol_startPublishCommon(pubclient, &p, qos, retained);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Copy and store message data for retries
* @param publish the publication data
* @param mm - pointer to the message data to store
* @param qos the MQTT QoS to use
* @param retained boolean - whether to set the MQTT retained flag
* @param allocatePayload boolean - whether or not to malloc payload
* @return pointer to the message data stored
*/
Messages* MQTTProtocol_createMessage(Publish* publish, Messages **mm, int qos, int retained, int allocatePayload)
{
Messages* m = malloc(sizeof(Messages));
FUNC_ENTRY;
if (!m)
goto exit;
m->len = sizeof(Messages);
if (*mm == NULL || (*mm)->publish == NULL)
{
int len1;
*mm = m;
if ((m->publish = MQTTProtocol_storePublication(publish, &len1)) == NULL)
{
free(m);
goto exit;
}
m->len += len1;
if (allocatePayload)
{
char *temp = m->publish->payload;
if ((m->publish->payload = malloc(m->publish->payloadlen)) == NULL)
{
free(m);
goto exit;
}
memcpy(m->publish->payload, temp, m->publish->payloadlen);
}
}
else /* this is now never used, I think */
{
++(((*mm)->publish)->refcount);
m->publish = (*mm)->publish;
}
m->msgid = publish->msgId;
m->qos = qos;
m->retain = retained;
m->MQTTVersion = publish->MQTTVersion;
if (m->MQTTVersion >= 5)
m->properties = MQTTProperties_copy(&publish->properties);
m->lastTouch = MQTTTime_now();
if (qos == 2)
m->nextMessageType = PUBREC;
exit:
FUNC_EXIT;
return m;
}
/**
* Store message data for possible retry
* @param publish the publication data
* @param len returned length of the data stored
* @return the publication stored
*/
Publications* MQTTProtocol_storePublication(Publish* publish, int* len)
{
Publications* p = malloc(sizeof(Publications));
FUNC_ENTRY;
if (!p)
goto exit;
p->refcount = 1;
*len = (int)strlen(publish->topic)+1;
p->topic = publish->topic;
publish->topic = NULL;
*len += sizeof(Publications);
p->topiclen = publish->topiclen;
p->payloadlen = publish->payloadlen;
p->payload = publish->payload;
publish->payload = NULL;
*len += publish->payloadlen;
if ((ListAppend(&(state.publications), p, *len)) == NULL)
{
free(p);
p = NULL;
}
exit:
FUNC_EXIT;
return p;
}
/**
* Remove stored message data. Opposite of storePublication
* @param p stored publication to remove
*/
void MQTTProtocol_removePublication(Publications* p)
{
FUNC_ENTRY;
if (p && --(p->refcount) == 0)
{
free(p->payload);
free(p->topic);
ListRemove(&(state.publications), p);
}
FUNC_EXIT;
}
/**
* Process an incoming publish packet for a socket
* The payload field of the packet has not been transferred to another buffer at this point.
* If it's needed beyond the scope of this function, it has to be copied.
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handlePublishes(void* pack, int sock)
{
Publish* publish = (Publish*)pack;
Clients* client = NULL;
char* clientid = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
clientid = client->clientID;
Log(LOG_PROTOCOL, 11, NULL, sock, clientid, publish->msgId, publish->header.bits.qos,
publish->header.bits.retain, publish->payloadlen, min(20, publish->payloadlen), publish->payload);
if (publish->header.bits.qos == 0)
Protocol_processPublication(publish, client, 1);
else if (!Socket_noPendingWrites(sock))
rc = SOCKET_ERROR; /* queue acks? */
else if (publish->header.bits.qos == 1)
{
/* send puback before processing the publications because a lot of return publications could fill up the socket buffer */
rc = MQTTPacket_send_puback(publish->MQTTVersion, publish->msgId, &client->net, client->clientID);
/* if we get a socket error from sending the puback, should we ignore the publication? */
Protocol_processPublication(publish, client, 1);
}
else if (publish->header.bits.qos == 2)
{
/* store publication in inbound list */
int len;
int already_received = 0;
ListElement* listElem = NULL;
Messages* m = malloc(sizeof(Messages));
Publications* p = NULL;
if (!m)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
p = MQTTProtocol_storePublication(publish, &len);
m->publish = p;
m->msgid = publish->msgId;
m->qos = publish->header.bits.qos;
m->retain = publish->header.bits.retain;
m->MQTTVersion = publish->MQTTVersion;
if (m->MQTTVersion >= MQTTVERSION_5)
m->properties = MQTTProperties_copy(&publish->properties);
m->nextMessageType = PUBREL;
if ((listElem = ListFindItem(client->inboundMsgs, &(m->msgid), messageIDCompare)) != NULL)
{ /* discard queued publication with same msgID that the current incoming message */
Messages* msg = (Messages*)(listElem->content);
MQTTProtocol_removePublication(msg->publish);
if (msg->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&msg->properties);
ListInsert(client->inboundMsgs, m, sizeof(Messages) + len, listElem);
ListRemove(client->inboundMsgs, msg);
already_received = 1;
} else
ListAppend(client->inboundMsgs, m, sizeof(Messages) + len);
rc = MQTTPacket_send_pubrec(publish->MQTTVersion, publish->msgId, &client->net, client->clientID);
if (m->MQTTVersion >= MQTTVERSION_5 && already_received == 0)
{
Publish publish1;
publish1.header.bits.qos = m->qos;
publish1.header.bits.retain = m->retain;
publish1.msgId = m->msgid;
publish1.topic = m->publish->topic;
publish1.topiclen = m->publish->topiclen;
publish1.payload = m->publish->payload;
publish1.payloadlen = m->publish->payloadlen;
publish1.MQTTVersion = m->MQTTVersion;
publish1.properties = m->properties;
Protocol_processPublication(&publish1, client, 1);
ListRemove(&(state.publications), m->publish);
m->publish = NULL;
} else
{ /* allocate and copy payload data as it's needed for pubrel.
For other cases, it's done in Protocol_processPublication */
char *temp = m->publish->payload;
if ((m->publish->payload = malloc(m->publish->payloadlen)) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
memcpy(m->publish->payload, temp, m->publish->payloadlen);
}
publish->topic = NULL;
}
exit:
MQTTPacket_freePublish(publish);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Process an incoming puback packet for a socket
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handlePubacks(void* pack, int sock)
{
Puback* puback = (Puback*)pack;
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 14, NULL, sock, client->clientID, puback->msgId);
/* look for the message by message id in the records of outbound messages for this client */
if (ListFindItem(client->outboundMsgs, &(puback->msgId), messageIDCompare) == NULL)
Log(TRACE_MIN, 3, NULL, "PUBACK", client->clientID, puback->msgId);
else
{
Messages* m = (Messages*)(client->outboundMsgs->current->content);
if (m->qos != 1)
Log(TRACE_MIN, 4, NULL, "PUBACK", client->clientID, puback->msgId, m->qos);
else
{
Log(TRACE_MIN, 6, NULL, "PUBACK", client->clientID, puback->msgId);
#if !defined(NO_PERSISTENCE)
rc = MQTTPersistence_remove(client,
(m->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_SENT : PERSISTENCE_PUBLISH_SENT,
m->qos, puback->msgId);
#endif
MQTTProtocol_removePublication(m->publish);
if (m->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&m->properties);
ListRemove(client->outboundMsgs, m);
}
}
if (puback->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&puback->properties);
free(pack);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Process an incoming pubrec packet for a socket
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handlePubrecs(void* pack, int sock)
{
Pubrec* pubrec = (Pubrec*)pack;
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 15, NULL, sock, client->clientID, pubrec->msgId);
/* look for the message by message id in the records of outbound messages for this client */
client->outboundMsgs->current = NULL;
if (ListFindItem(client->outboundMsgs, &(pubrec->msgId), messageIDCompare) == NULL)
{
if (pubrec->header.bits.dup == 0)
Log(TRACE_MIN, 3, NULL, "PUBREC", client->clientID, pubrec->msgId);
}
else
{
Messages* m = (Messages*)(client->outboundMsgs->current->content);
if (m->qos != 2)
{
if (pubrec->header.bits.dup == 0)
Log(TRACE_MIN, 4, NULL, "PUBREC", client->clientID, pubrec->msgId, m->qos);
}
else if (m->nextMessageType != PUBREC)
{
if (pubrec->header.bits.dup == 0)
Log(TRACE_MIN, 5, NULL, "PUBREC", client->clientID, pubrec->msgId);
}
else
{
if (pubrec->MQTTVersion >= MQTTVERSION_5 && pubrec->rc >= MQTTREASONCODE_UNSPECIFIED_ERROR)
{
Log(TRACE_MIN, -1, "Pubrec error %d received for client %s msgid %d, not sending PUBREL",
pubrec->rc, client->clientID, pubrec->msgId);
#if !defined(NO_PERSISTENCE)
rc = MQTTPersistence_remove(client,
(pubrec->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_SENT : PERSISTENCE_PUBLISH_SENT,
m->qos, pubrec->msgId);
#endif
MQTTProtocol_removePublication(m->publish);
if (m->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&m->properties);
ListRemove(client->outboundMsgs, m);
(++state.msgs_sent);
}
else
{
rc = MQTTPacket_send_pubrel(pubrec->MQTTVersion, pubrec->msgId, 0, &client->net, client->clientID);
m->nextMessageType = PUBCOMP;
m->lastTouch = MQTTTime_now();
}
}
}
if (pubrec->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&pubrec->properties);
free(pack);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Process an incoming pubrel packet for a socket
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handlePubrels(void* pack, int sock)
{
Pubrel* pubrel = (Pubrel*)pack;
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 17, NULL, sock, client->clientID, pubrel->msgId);
/* look for the message by message id in the records of inbound messages for this client */
if (ListFindItem(client->inboundMsgs, &(pubrel->msgId), messageIDCompare) == NULL)
{
if (pubrel->header.bits.dup == 0)
Log(TRACE_MIN, 3, NULL, "PUBREL", client->clientID, pubrel->msgId);
else if (!Socket_noPendingWrites(sock))
rc = SOCKET_ERROR; /* queue acks? */
else
/* Apparently this is "normal" behaviour, so we don't need to issue a warning */
rc = MQTTPacket_send_pubcomp(pubrel->MQTTVersion, pubrel->msgId, &client->net, client->clientID);
}
else
{
Messages* m = (Messages*)(client->inboundMsgs->current->content);
if (m->qos != 2)
Log(TRACE_MIN, 4, NULL, "PUBREL", client->clientID, pubrel->msgId, m->qos);
else if (m->nextMessageType != PUBREL)
Log(TRACE_MIN, 5, NULL, "PUBREL", client->clientID, pubrel->msgId);
else if (!Socket_noPendingWrites(sock))
rc = SOCKET_ERROR; /* queue acks? */
else
{
Publish publish;
memset(&publish, '\0', sizeof(publish));
/* send pubcomp before processing the publications because a lot of return publications could fill up the socket buffer */
rc = MQTTPacket_send_pubcomp(pubrel->MQTTVersion, pubrel->msgId, &client->net, client->clientID);
publish.header.bits.qos = m->qos;
publish.header.bits.retain = m->retain;
publish.msgId = m->msgid;
if (m->publish)
{
publish.topic = m->publish->topic;
publish.topiclen = m->publish->topiclen;
publish.payload = m->publish->payload;
publish.payloadlen = m->publish->payloadlen;
}
publish.MQTTVersion = m->MQTTVersion;
if (publish.MQTTVersion >= MQTTVERSION_5)
publish.properties = m->properties;
else
Protocol_processPublication(&publish, client, 0); /* only for 3.1.1 and lower */
#if !defined(NO_PERSISTENCE)
rc += MQTTPersistence_remove(client,
(m->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_RECEIVED : PERSISTENCE_PUBLISH_RECEIVED,
m->qos, pubrel->msgId);
#endif
if (m->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&m->properties);
if (m->publish)
ListRemove(&(state.publications), m->publish);
ListRemove(client->inboundMsgs, m);
++(state.msgs_received);
}
}
if (pubrel->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&pubrel->properties);
free(pack);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Process an incoming pubcomp packet for a socket
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handlePubcomps(void* pack, int sock)
{
Pubcomp* pubcomp = (Pubcomp*)pack;
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 19, NULL, sock, client->clientID, pubcomp->msgId);
/* look for the message by message id in the records of outbound messages for this client */
if (ListFindItem(client->outboundMsgs, &(pubcomp->msgId), messageIDCompare) == NULL)
{
if (pubcomp->header.bits.dup == 0)
Log(TRACE_MIN, 3, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
}
else
{
Messages* m = (Messages*)(client->outboundMsgs->current->content);
if (m->qos != 2)
Log(TRACE_MIN, 4, NULL, "PUBCOMP", client->clientID, pubcomp->msgId, m->qos);
else
{
if (m->nextMessageType != PUBCOMP)
Log(TRACE_MIN, 5, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
else
{
Log(TRACE_MIN, 6, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
#if !defined(NO_PERSISTENCE)
rc = MQTTPersistence_remove(client,
(m->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_SENT : PERSISTENCE_PUBLISH_SENT,
m->qos, pubcomp->msgId);
if (rc != 0)
Log(LOG_ERROR, -1, "Error removing PUBCOMP for client id %s msgid %d from persistence", client->clientID, pubcomp->msgId);
#endif
MQTTProtocol_removePublication(m->publish);
if (m->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&m->properties);
ListRemove(client->outboundMsgs, m);
(++state.msgs_sent);
}
}
}
if (pubcomp->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&pubcomp->properties);
free(pack);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* MQTT protocol keepAlive processing. Sends PINGREQ packets as required.
* @param now current time
*/
void MQTTProtocol_keepalive(START_TIME_TYPE now)
{
ListElement* current = NULL;
FUNC_ENTRY;
ListNextElement(bstate->clients, &current);
while (current)
{
Clients* client = (Clients*)(current->content);
ListNextElement(bstate->clients, &current);
if (client->connected == 0 || client->keepAliveInterval == 0)
continue;
if (client->ping_outstanding == 1)
{
if (MQTTTime_difftime(now, client->net.lastPing) >= (long)(client->keepAliveInterval * 1000))
{
Log(TRACE_PROTOCOL, -1, "PINGRESP not received in keepalive interval for client %s on socket %d, disconnecting", client->clientID, client->net.socket);
MQTTProtocol_closeSession(client, 1);
}
}
else if (MQTTTime_difftime(now, client->net.lastSent) >= (long)(client->keepAliveInterval * 1000) ||
MQTTTime_difftime(now, client->net.lastReceived) >= (long)(client->keepAliveInterval * 1000))
{
if (Socket_noPendingWrites(client->net.socket))
{
if (MQTTPacket_send_pingreq(&client->net, client->clientID) != TCPSOCKET_COMPLETE)
{
Log(TRACE_PROTOCOL, -1, "Error sending PINGREQ for client %s on socket %d, disconnecting", client->clientID, client->net.socket);
MQTTProtocol_closeSession(client, 1);
}
else
{
client->net.lastPing = now;
client->ping_outstanding = 1;
}
}
}
}
FUNC_EXIT;
}
/**
* MQTT retry processing per client
* @param now current time
* @param client - the client to which to apply the retry processing
* @param regardless boolean - retry packets regardless of retry interval (used on reconnect)
*/
static void MQTTProtocol_retries(START_TIME_TYPE now, Clients* client, int regardless)
{
ListElement* outcurrent = NULL;
FUNC_ENTRY;
if (!regardless && client->retryInterval <= 0) /* 0 or -ive retryInterval turns off retry except on reconnect */
goto exit;
while (client && ListNextElement(client->outboundMsgs, &outcurrent) &&
client->connected && client->good && /* client is connected and has no errors */
Socket_noPendingWrites(client->net.socket)) /* there aren't any previous packets still stacked up on the socket */
{
Messages* m = (Messages*)(outcurrent->content);
if (regardless || MQTTTime_difftime(now, m->lastTouch) > (long)(max(client->retryInterval, 10) * 1000))
{
if (m->qos == 1 || (m->qos == 2 && m->nextMessageType == PUBREC))
{
Publish publish;
int rc;
Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->net.socket, m->msgid);
publish.msgId = m->msgid;
publish.topic = m->publish->topic;
publish.payload = m->publish->payload;
publish.payloadlen = m->publish->payloadlen;
publish.properties = m->properties;
publish.MQTTVersion = m->MQTTVersion;
rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, &client->net, client->clientID);
if (rc == SOCKET_ERROR)
{
client->good = 0;
Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket,
Socket_getpeer(client->net.socket));
MQTTProtocol_closeSession(client, 1);
client = NULL;
}
else
{
if (m->qos == 0 && rc == TCPSOCKET_INTERRUPTED)
MQTTProtocol_storeQoS0(client, &publish);
m->lastTouch = MQTTTime_now();
}
}
else if (m->qos && m->nextMessageType == PUBCOMP)
{
Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->net.socket, m->msgid);
if (MQTTPacket_send_pubrel(m->MQTTVersion, m->msgid, 0, &client->net, client->clientID) != TCPSOCKET_COMPLETE)
{
client->good = 0;
Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket,
Socket_getpeer(client->net.socket));
MQTTProtocol_closeSession(client, 1);
client = NULL;
}
else
m->lastTouch = MQTTTime_now();
}
/* break; why not do all retries at once? */
}
}
exit:
FUNC_EXIT;
}
/**
* MQTT retry protocol and socket pending writes processing.
* @param now current time
* @param doRetry boolean - retries as well as pending writes?
* @param regardless boolean - retry packets regardless of retry interval (used on reconnect)
*/
void MQTTProtocol_retry(START_TIME_TYPE now, int doRetry, int regardless)
{
ListElement* current = NULL;
FUNC_ENTRY;
ListNextElement(bstate->clients, &current);
/* look through the outbound message list of each client, checking to see if a retry is necessary */
while (current)
{
Clients* client = (Clients*)(current->content);
ListNextElement(bstate->clients, &current);
if (client->connected == 0)
continue;
if (client->good == 0)
{
MQTTProtocol_closeSession(client, 1);
continue;
}
if (Socket_noPendingWrites(client->net.socket) == 0)
continue;
if (doRetry)
MQTTProtocol_retries(now, client, regardless);
}
FUNC_EXIT;
}
/**
* Free a client structure
* @param client the client data to free
*/
void MQTTProtocol_freeClient(Clients* client)
{
FUNC_ENTRY;
/* free up pending message lists here, and any other allocated data */
MQTTProtocol_freeMessageList(client->outboundMsgs);
MQTTProtocol_freeMessageList(client->inboundMsgs);
ListFree(client->messageQueue);
free(client->clientID);
client->clientID = NULL;
if (client->will)
{
free(client->will->payload);
free(client->will->topic);
free(client->will);
client->will = NULL;
}
if (client->username)
free((void*)client->username);
if (client->password)
free((void*)client->password);
#if defined(OPENSSL)
if (client->sslopts)
{
if (client->sslopts->trustStore)
free((void*)client->sslopts->trustStore);
if (client->sslopts->keyStore)
free((void*)client->sslopts->keyStore);
if (client->sslopts->privateKey)
free((void*)client->sslopts->privateKey);
if (client->sslopts->privateKeyPassword)
free((void*)client->sslopts->privateKeyPassword);
if (client->sslopts->enabledCipherSuites)
free((void*)client->sslopts->enabledCipherSuites);
if (client->sslopts->struct_version >= 2)
{
if (client->sslopts->CApath)
free((void*)client->sslopts->CApath);
}
free(client->sslopts);
client->sslopts = NULL;
}
#endif
/* don't free the client structure itself... this is done elsewhere */
FUNC_EXIT;
}
/**
* Empty a message list, leaving it able to accept new messages
* @param msgList the message list to empty
*/
void MQTTProtocol_emptyMessageList(List* msgList)
{
ListElement* current = NULL;
FUNC_ENTRY;
while (ListNextElement(msgList, &current))
{
Messages* m = (Messages*)(current->content);
MQTTProtocol_removePublication(m->publish);
if (m->MQTTVersion >= MQTTVERSION_5)
MQTTProperties_free(&m->properties);
}
ListEmpty(msgList);
FUNC_EXIT;
}
/**
* Empty and free up all storage used by a message list
* @param msgList the message list to empty and free
*/
void MQTTProtocol_freeMessageList(List* msgList)
{
FUNC_ENTRY;
MQTTProtocol_emptyMessageList(msgList);
ListFree(msgList);
FUNC_EXIT;
}
/**
* Copy no more than dest_size -1 characters from the string pointed to by src to the array pointed to by dest.
* The destination string will always be null-terminated.
* @param dest the array which characters copy to
* @param src the source string which characters copy from
* @param dest_size the size of the memory pointed to by dest: copy no more than this -1 (allow for null). Must be >= 1
* @return the destination string pointer
*/
char* MQTTStrncpy(char *dest, const char *src, size_t dest_size)
{
size_t count = dest_size;
char *temp = dest;
FUNC_ENTRY;
if (dest_size < strlen(src))
Log(TRACE_MIN, -1, "the src string is truncated");
/* We must copy only the first (dest_size - 1) bytes */
while (count > 1 && (*temp++ = *src++))
count--;
*temp = '\0';
FUNC_EXIT;
return dest;
}
/**
* Duplicate a string, safely, allocating space on the heap
* @param src the source string which characters copy from
* @return the duplicated, allocated string
*/
char* MQTTStrdup(const char* src)
{
size_t mlen = strlen(src) + 1;
char* temp = malloc(mlen);
if (temp)
MQTTStrncpy(temp, src, mlen);
return temp;
}

418
deps/paho.mqtt.c/src/MQTTProtocolOut.c vendored Normal file
View File

@ -0,0 +1,418 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
* Ian Craggs - fix for buffer overflow in addressPort bug #433290
* Ian Craggs - MQTT 3.1.1 support
* Rong Xiang, Ian Craggs - C++ compatibility
* Ian Craggs - fix for bug 479376
* Ian Craggs - SNI support
* Ian Craggs - fix for issue #164
* Ian Craggs - fix for issue #179
* Ian Craggs - MQTT 5.0 support
*******************************************************************************/
/**
* @file
* \brief Functions dealing with the MQTT protocol exchanges
*
* Some other related functions are in the MQTTProtocolClient module
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "MQTTProtocolOut.h"
#include "StackTrace.h"
#include "Heap.h"
#include "WebSocket.h"
#include "Base64.h"
extern ClientStates* bstate;
/**
* Separates an address:port into two separate values
* @param[in] uri the input string - hostname:port
* @param[out] port the returned port integer
* @param[out] topic optional topic portion of the address starting with '/'
* @return the address string
*/
size_t MQTTProtocol_addressPort(const char* uri, int* port, const char **topic)
{
char* colon_pos = strrchr(uri, ':'); /* reverse find to allow for ':' in IPv6 addresses */
char* buf = (char*)uri;
size_t len;
FUNC_ENTRY;
if (uri[0] == '[')
{ /* ip v6 */
if (colon_pos < strrchr(uri, ']'))
colon_pos = NULL; /* means it was an IPv6 separator, not for host:port */
}
if (colon_pos) /* have to strip off the port */
{
len = colon_pos - uri;
*port = atoi(colon_pos + 1);
}
else
{
len = strlen(buf);
*port = DEFAULT_PORT;
}
/* try and find topic portion */
if ( topic )
{
const char* addr_start = uri;
if ( colon_pos )
addr_start = colon_pos;
*topic = strchr( addr_start, '/' );
}
if (buf[len - 1] == ']')
{
/* we are stripping off the final ], so length is 1 shorter */
--len;
}
FUNC_EXIT;
return len;
}
/**
* Allow user or password characters to be expressed in the form of %XX, XX being the
* hexadecimal value of the caracter. This will avoid problems when a user code or a password
* contains a '@' or another special character ('%' included)
* @param p0
* @param p1
* @param basic_auth_in_len
*/
void MQTTProtocol_specialChars(char* p0, char* p1, b64_size_t *basic_auth_in_len)
{
while(*p1 != '@') {
if (*p1 != '%') {
*p0++ = *p1++;
}
else if (isxdigit(*(p1 + 1)) && isxdigit(*(p1 + 2))) {
/* next 2 characters are hexa digits */
char hex[3];
p1++;
hex[0] = *p1++;
hex[1] = *p1++;
hex[2] = '\0';
*p0++ = (char)strtol(hex, 0, 16);
/* 3 input char => 1 output char */
*basic_auth_in_len -= 2;
}
}
*p0 = 0x0;
}
/**
* MQTT outgoing connect processing for a client
* @param ip_address the TCP address:port to connect to
* @param aClient a structure with all MQTT data needed
* @param int ssl
* @param int MQTTVersion the MQTT version to connect with (3 or 4)
* @param long timeout how long to wait for a new socket to be created
* @return return code
*/
#if defined(OPENSSL)
#if defined(__GNUC__) && defined(__linux__)
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int ssl, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout)
#else
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int ssl, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties)
#endif
#else
#if defined(__GNUC__) && defined(__linux__)
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout)
#else
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket, int MQTTVersion,
MQTTProperties* connectProperties, MQTTProperties* willProperties)
#endif
#endif
{
int rc, port;
size_t addr_len;
b64_size_t basic_auth_in_len, basic_auth_out_len;
char *p0, *p1;
b64_data_t *basic_auth;
FUNC_ENTRY;
aClient->good = 1;
aClient->net.http_proxy = NULL;
aClient->net.http_proxy_auth = NULL;
if ((p0 = getenv("http_proxy")))
{
p1 = strchr(p0, '@');
if(p1)
{
aClient->net.http_proxy = p1 + 1;
p1 = strchr(p0, ':') + 3;
basic_auth_in_len = (b64_size_t)(aClient->net.http_proxy - p1);
basic_auth = (b64_data_t *)malloc(sizeof(char)*basic_auth_in_len);
if (!basic_auth)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
basic_auth_in_len--;
p0 = (char *)basic_auth;
MQTTProtocol_specialChars(p0, p1, &basic_auth_in_len);
basic_auth_out_len = Base64_encodeLength(basic_auth, basic_auth_in_len);
if ((aClient->net.http_proxy_auth = (char *)malloc(sizeof(char) * basic_auth_out_len)) == NULL)
{
free(basic_auth);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
Base64_encode(aClient->net.http_proxy_auth, basic_auth_out_len, basic_auth, basic_auth_in_len);
free(basic_auth);
}
else {
aClient->net.http_proxy = strchr(p0, ':') + 3;
}
}
#if defined(OPENSSL)
aClient->net.https_proxy = NULL;
aClient->net.https_proxy_auth = NULL;
if ((p0 = getenv("https_proxy"))) {
p1 = strchr(p0, '@');
if(p1) {
aClient->net.https_proxy = p1 + 1;
p1 = strchr(p0, ':') + 3;
basic_auth_in_len = (b64_size_t)(aClient->net.https_proxy - p1);
basic_auth = (b64_data_t *)malloc(sizeof(char)*basic_auth_in_len);
if (!basic_auth)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
basic_auth_in_len--;
p0 = (char *)basic_auth;
MQTTProtocol_specialChars(p0, p1, &basic_auth_in_len);
basic_auth_out_len = Base64_encodeLength(basic_auth, basic_auth_in_len);
if ((aClient->net.https_proxy_auth = (char *)malloc(sizeof(char) * basic_auth_out_len)) == NULL)
{
free(basic_auth);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
Base64_encode(aClient->net.https_proxy_auth, basic_auth_out_len, basic_auth, basic_auth_in_len);
free(basic_auth);
}
else {
aClient->net.https_proxy = strchr(p0, ':') + 3;
}
}
if (!ssl && websocket && aClient->net.http_proxy) {
#else
if (websocket && aClient->net.http_proxy) {
#endif
addr_len = MQTTProtocol_addressPort(aClient->net.http_proxy, &port, NULL);
#if defined(__GNUC__) && defined(__linux__)
if (timeout < 0)
rc = -1;
else
rc = Socket_new(aClient->net.http_proxy, addr_len, port, &(aClient->net.socket), timeout);
#else
rc = Socket_new(aClient->net.http_proxy, addr_len, port, &(aClient->net.socket));
#endif
}
#if defined(OPENSSL)
else if (ssl && websocket && aClient->net.https_proxy) {
addr_len = MQTTProtocol_addressPort(aClient->net.https_proxy, &port, NULL);
#if defined(__GNUC__) && defined(__linux__)
if (timeout < 0)
rc = -1;
else
rc = Socket_new(aClient->net.https_proxy, addr_len, port, &(aClient->net.socket), timeout);
#else
rc = Socket_new(aClient->net.https_proxy, addr_len, port, &(aClient->net.socket));
#endif
}
#endif
else {
addr_len = MQTTProtocol_addressPort(ip_address, &port, NULL);
#if defined(__GNUC__) && defined(__linux__)
if (timeout < 0)
rc = -1;
else
rc = Socket_new(ip_address, addr_len, port, &(aClient->net.socket), timeout);
#else
rc = Socket_new(ip_address, addr_len, port, &(aClient->net.socket));
#endif
}
if (rc == EINPROGRESS || rc == EWOULDBLOCK)
aClient->connect_state = TCP_IN_PROGRESS; /* TCP connect called - wait for connect completion */
else if (rc == 0)
{ /* TCP connect completed. If SSL, send SSL connect */
#if defined(OPENSSL)
if (ssl)
{
if (websocket && aClient->net.https_proxy) {
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
rc = WebSocket_proxy_connect( &aClient->net, 1, ip_address);
}
if (rc == 0 && SSLSocket_setSocketForSSL(&aClient->net, aClient->sslopts, ip_address, addr_len) == 1)
{
rc = aClient->sslopts->struct_version >= 3 ?
SSLSocket_connect(aClient->net.ssl, aClient->net.socket, ip_address,
aClient->sslopts->verify, aClient->sslopts->ssl_error_cb, aClient->sslopts->ssl_error_context) :
SSLSocket_connect(aClient->net.ssl, aClient->net.socket, ip_address,
aClient->sslopts->verify, NULL, NULL);
if (rc == TCPSOCKET_INTERRUPTED)
aClient->connect_state = SSL_IN_PROGRESS; /* SSL connect called - wait for completion */
}
else
rc = SOCKET_ERROR;
}
else if (websocket && aClient->net.http_proxy) {
#else
if (websocket && aClient->net.http_proxy) {
#endif
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
rc = WebSocket_proxy_connect( &aClient->net, 0, ip_address);
}
if ( websocket )
{
rc = WebSocket_connect( &aClient->net, ip_address );
if ( rc == TCPSOCKET_INTERRUPTED )
aClient->connect_state = WEBSOCKET_IN_PROGRESS; /* Websocket connect called - wait for completion */
}
if (rc == 0)
{
/* Now send the MQTT connect packet */
if ((rc = MQTTPacket_send_connect(aClient, MQTTVersion, connectProperties, willProperties)) == 0)
aClient->connect_state = WAIT_FOR_CONNACK; /* MQTT Connect sent - wait for CONNACK */
else
aClient->connect_state = NOT_IN_PROGRESS;
}
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Process an incoming pingresp packet for a socket
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handlePingresps(void* pack, int sock)
{
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 21, NULL, sock, client->clientID);
client->ping_outstanding = 0;
FUNC_EXIT_RC(rc);
return rc;
}
/**
* MQTT outgoing subscribe processing for a client
* @param client the client structure
* @param topics list of topics
* @param qoss corresponding list of QoSs
* @param opts MQTT 5.0 subscribe options
* @param props MQTT 5.0 subscribe properties
* @return completion code
*/
int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss, int msgID,
MQTTSubscribe_options* opts, MQTTProperties* props)
{
int rc = 0;
FUNC_ENTRY;
rc = MQTTPacket_send_subscribe(topics, qoss, opts, props, msgID, 0, client);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Process an incoming suback packet for a socket
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handleSubacks(void* pack, int sock)
{
Suback* suback = (Suback*)pack;
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 23, NULL, sock, client->clientID, suback->msgId);
MQTTPacket_freeSuback(suback);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* MQTT outgoing unsubscribe processing for a client
* @param client the client structure
* @param topics list of topics
* @return completion code
*/
int MQTTProtocol_unsubscribe(Clients* client, List* topics, int msgID, MQTTProperties* props)
{
int rc = 0;
FUNC_ENTRY;
rc = MQTTPacket_send_unsubscribe(topics, props, msgID, 0, client);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Process an incoming unsuback packet for a socket
* @param pack pointer to the publish packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handleUnsubacks(void* pack, int sock)
{
Unsuback* unsuback = (Unsuback*)pack;
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 24, NULL, sock, client->clientID, unsuback->msgId);
MQTTPacket_freeUnsuback(unsuback);
FUNC_EXIT_RC(rc);
return rc;
}

100
deps/paho.mqtt.c/src/MQTTReasonCodes.c vendored Normal file
View File

@ -0,0 +1,100 @@
/*******************************************************************************
* 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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#include "MQTTReasonCodes.h"
#include "Heap.h"
#include "StackTrace.h"
#include <memory.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static struct {
enum MQTTReasonCodes value;
const char* name;
} nameToString[] =
{
{MQTTREASONCODE_SUCCESS, "SUCCESS"},
{MQTTREASONCODE_NORMAL_DISCONNECTION, "Normal disconnection"},
{MQTTREASONCODE_GRANTED_QOS_0, "Granted QoS 0"},
{MQTTREASONCODE_GRANTED_QOS_1, "Granted QoS 1"},
{MQTTREASONCODE_GRANTED_QOS_2, "Granted QoS 2"},
{MQTTREASONCODE_DISCONNECT_WITH_WILL_MESSAGE, "Disconnect with Will Message"},
{MQTTREASONCODE_NO_MATCHING_SUBSCRIBERS, "No matching subscribers"},
{MQTTREASONCODE_NO_SUBSCRIPTION_FOUND, "No subscription found"},
{MQTTREASONCODE_CONTINUE_AUTHENTICATION, "Continue authentication"},
{MQTTREASONCODE_RE_AUTHENTICATE, "Re-authenticate"},
{MQTTREASONCODE_UNSPECIFIED_ERROR, "Unspecified error"},
{MQTTREASONCODE_MALFORMED_PACKET, "Malformed Packet"},
{MQTTREASONCODE_PROTOCOL_ERROR, "Protocol error"},
{MQTTREASONCODE_IMPLEMENTATION_SPECIFIC_ERROR, "Implementation specific error"},
{MQTTREASONCODE_UNSUPPORTED_PROTOCOL_VERSION, "Unsupported Protocol Version"},
{MQTTREASONCODE_CLIENT_IDENTIFIER_NOT_VALID, "Client Identifier not valid"},
{MQTTREASONCODE_BAD_USER_NAME_OR_PASSWORD, "Bad User Name or Password"},
{MQTTREASONCODE_NOT_AUTHORIZED, "Not authorized"},
{MQTTREASONCODE_SERVER_UNAVAILABLE, "Server unavailable"},
{MQTTREASONCODE_SERVER_BUSY, "Server busy"},
{MQTTREASONCODE_BANNED, "Banned"},
{MQTTREASONCODE_SERVER_SHUTTING_DOWN, "Server shutting down"},
{MQTTREASONCODE_BAD_AUTHENTICATION_METHOD, "Bad authentication method"},
{MQTTREASONCODE_KEEP_ALIVE_TIMEOUT, "Keep Alive timeout"},
{MQTTREASONCODE_SESSION_TAKEN_OVER, "Session taken over"},
{MQTTREASONCODE_TOPIC_FILTER_INVALID, "Topic filter invalid"},
{MQTTREASONCODE_TOPIC_NAME_INVALID, "Topic name invalid"},
{MQTTREASONCODE_PACKET_IDENTIFIER_IN_USE, "Packet Identifier in use"},
{MQTTREASONCODE_PACKET_IDENTIFIER_NOT_FOUND, "Packet Identifier not found"},
{MQTTREASONCODE_RECEIVE_MAXIMUM_EXCEEDED, "Receive Maximum exceeded"},
{MQTTREASONCODE_TOPIC_ALIAS_INVALID, "Topic Alias invalid"},
{MQTTREASONCODE_PACKET_TOO_LARGE, "Packet too large"},
{MQTTREASONCODE_MESSAGE_RATE_TOO_HIGH, "Message rate too high"},
{MQTTREASONCODE_QUOTA_EXCEEDED, "Quota exceeded"},
{MQTTREASONCODE_ADMINISTRATIVE_ACTION, "Administrative action"},
{MQTTREASONCODE_PAYLOAD_FORMAT_INVALID, "Payload format invalid"},
{MQTTREASONCODE_RETAIN_NOT_SUPPORTED, "Retain not supported"},
{MQTTREASONCODE_QOS_NOT_SUPPORTED, "QoS not supported"},
{MQTTREASONCODE_USE_ANOTHER_SERVER, "Use another server"},
{MQTTREASONCODE_SERVER_MOVED, "Server moved"},
{MQTTREASONCODE_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED, "Shared subscriptions not supported"},
{MQTTREASONCODE_CONNECTION_RATE_EXCEEDED, "Connection rate exceeded"},
{MQTTREASONCODE_MAXIMUM_CONNECT_TIME, "Maximum connect time"},
{MQTTREASONCODE_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED, "Subscription Identifiers not supported"},
{MQTTREASONCODE_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED, "Wildcard Subscriptions not supported"}
};
const char* MQTTReasonCode_toString(enum MQTTReasonCodes value)
{
int i = 0;
const char* result = NULL;
for (i = 0; i < ARRAY_SIZE(nameToString); ++i)
{
if (nameToString[i].value == value)
{
result = nameToString[i].name;
break;
}
}
return result;
}

124
deps/paho.mqtt.c/src/MQTTTime.c vendored Normal file
View File

@ -0,0 +1,124 @@
/*******************************************************************************
* Copyright (c) 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial implementation
*******************************************************************************/
#include "MQTTTime.h"
#include "StackTrace.h"
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <unistd.h>
#include <sys/time.h>
#endif
void MQTTTime_sleep(long milliseconds)
{
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
Sleep(milliseconds);
#else
usleep(milliseconds*1000);
#endif
FUNC_EXIT;
}
#if defined(_WIN32) || defined(_WIN64)
START_TIME_TYPE MQTTTime_start_clock(void)
{
return GetTickCount();
}
#elif defined(AIX)
START_TIME_TYPE MQTTTime_start_clock(void)
{
static struct timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);
return start;
}
#else
START_TIME_TYPE MQTTTime_start_clock(void)
{
static struct timeval start;
static struct timespec start_ts;
clock_gettime(CLOCK_MONOTONIC, &start_ts);
start.tv_sec = start_ts.tv_sec;
start.tv_usec = start_ts.tv_nsec / 1000;
return start;
}
#endif
START_TIME_TYPE MQTTTime_now(void)
{
return MQTTTime_start_clock();
}
#if defined(_WIN32) || defined(_WIN64)
long MQTTTime_elapsed(DWORD milliseconds)
{
return GetTickCount() - milliseconds;
}
#elif defined(AIX)
#define assert(a)
long MQTTTime_elapsed(struct timespec start)
{
struct timespec now, res;
clock_gettime(CLOCK_MONOTONIC, &now);
ntimersub(now, start, res);
return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L;
}
#else
long MQTTTime_elapsed(struct timeval start)
{
struct timeval now, res;
static struct timespec now_ts;
clock_gettime(CLOCK_MONOTONIC, &now_ts);
now.tv_sec = now_ts.tv_sec;
now.tv_usec = now_ts.tv_nsec / 1000;
timersub(&now, &start, &res);
return (res.tv_sec)*1000 + (res.tv_usec)/1000;
}
#endif
#if defined(_WIN32) || defined(_WIN64)
/*
* @param new most recent time in milliseconds from GetTickCount()
* @param old older time in milliseconds from GetTickCount()
* @return difference in milliseconds
*/
long MQTTTime_difftime(DWORD new, DWORD old)
{
return new - old;
}
#elif defined(AIX)
#define assert(a)
long MQTTTime_difftime(struct timespec new, struct timespec old)
{
struct timespec result;
ntimersub(new, old, result);
return (result.tv_sec)*1000L + (result.tv_nsec)/1000000L; /* convert to milliseconds */
}
#else
long MQTTTime_difftime(struct timeval new, struct timeval old)
{
struct timeval result;
timersub(&new, &old, &result);
return (result.tv_sec)*1000 + (result.tv_usec)/1000; /* convert to milliseconds */
}
#endif

230
deps/paho.mqtt.c/src/MQTTVersion.c vendored Normal file
View File

@ -0,0 +1,230 @@
/*******************************************************************************
* 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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#include <stdio.h>
#if !defined(_WRS_KERNEL)
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <ctype.h>
#include "MQTTAsync.h"
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <sys/stat.h>
#else
#include <dlfcn.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
/**
*
* @file
* \brief MQTTVersion - display the version and build information strings for a library.
*
* With no arguments, we try to load and call the version string for the libraries we
* know about: mqttv3c, mqttv3cs, mqttv3a, mqttv3as.
* With an argument:
* 1) we try to load the named library, call getVersionInfo and display those values.
* 2) If that doesn't work, we look through the binary for eyecatchers, and display those.
* This will work if the library is not executable in the current environment.
*
* */
static const char* libraries[] = {"paho-mqtt3c", "paho-mqtt3cs", "paho-mqtt3a", "paho-mqtt3as"};
static const char* eyecatchers[] = {"MQTTAsyncV3_Version", "MQTTAsyncV3_Timestamp",
"MQTTClientV3_Version", "MQTTClientV3_Timestamp"};
char* FindString(char* filename, const char* eyecatcher_input);
int printVersionInfo(MQTTAsync_nameValue* info);
int loadandcall(const char* libname);
void printEyecatchers(char* filename);
/**
* Finds an eyecatcher in a binary file and returns the following value.
* @param filename the name of the file
* @param eyecatcher_input the eyecatcher string to look for
* @return the value found - "" if not found
*/
char* FindString(char* filename, const char* eyecatcher_input)
{
FILE* infile = NULL;
static char value[100];
const char* eyecatcher = eyecatcher_input;
memset(value, 0, 100);
if ((infile = fopen(filename, "rb")) != NULL)
{
size_t buflen = strlen(eyecatcher);
char* buffer = (char*) malloc(buflen + 1); /* added space for unused null terminator to stop LGTM complaint */
if (buffer != NULL)
{
int c = fgetc(infile);
while (feof(infile) == 0)
{
int count = 0;
buffer[count++] = c;
if (memcmp(eyecatcher, buffer, buflen) == 0)
{
char* ptr = value;
c = fgetc(infile); /* skip space */
c = fgetc(infile);
while (isprint(c))
{
*ptr++ = c;
c = fgetc(infile);
}
break;
}
if (count == buflen)
{
memmove(buffer, &buffer[1], buflen - 1);
count--;
}
c = fgetc(infile);
}
free(buffer);
}
fclose(infile);
}
return value;
}
int printVersionInfo(MQTTAsync_nameValue* info)
{
int rc = 0;
while (info->name)
{
printf("%s: %s\n", info->name, info->value);
info++;
rc = 1; /* at least one value printed */
}
return rc;
}
typedef MQTTAsync_nameValue* (*func_type)(void);
int loadandcall(const char* libname)
{
int rc = 0;
MQTTAsync_nameValue* (*func_address)(void) = NULL;
#if defined(_WIN32) || defined(_WIN64)
HMODULE APILibrary = LoadLibraryA(libname);
if (APILibrary == NULL)
printf("Error loading library %s, error code %d\n", libname, GetLastError());
else
{
func_address = (func_type)GetProcAddress(APILibrary, "MQTTAsync_getVersionInfo");
if (func_address == NULL)
func_address = (func_type)GetProcAddress(APILibrary, "MQTTClient_getVersionInfo");
if (func_address)
rc = printVersionInfo((*func_address)());
FreeLibrary(APILibrary);
}
#else
void* APILibrary = dlopen(libname, RTLD_LAZY); /* Open the Library in question */
if (APILibrary == NULL)
printf("Error loading library %s, error %s\n", libname, dlerror());
else
{
*(void **) (&func_address) = dlsym(APILibrary, "MQTTAsync_getVersionInfo");
if (func_address == NULL)
func_address = dlsym(APILibrary, "MQTTClient_getVersionInfo");
if (func_address)
rc = printVersionInfo((*func_address)());
dlclose(APILibrary);
}
#endif
return rc;
}
#if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
void printEyecatchers(char* filename)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(eyecatchers); ++i)
{
char* value = FindString(filename, eyecatchers[i]);
if (value[0])
printf("%s: %s\n", eyecatchers[i], value);
}
}
int main(int argc, char** argv)
{
printf("MQTTVersion: print the version strings of an MQTT client library\n");
printf("Copyright (c) 2012, 2018 IBM Corp.\n");
if (argc == 1)
{
int i = 0;
char namebuf[60];
printf("Specify a particular library name if it is not in the current directory, or not executable on this platform\n");
for (i = 0; i < ARRAY_SIZE(libraries); ++i)
{
#if defined(__CYGWIN__)
sprintf(namebuf, "cyg%s-1.dll", libraries[i]);
#elif defined(_WIN32) || defined(_WIN64)
sprintf(namebuf, "%s.dll", libraries[i]);
#elif defined(OSX)
sprintf(namebuf, "lib%s.1.dylib", libraries[i]);
#else
sprintf(namebuf, "lib%s.so.1", libraries[i]);
#endif
printf("--- Trying library %s ---\n", libraries[i]);
if (!loadandcall(namebuf))
printEyecatchers(namebuf);
}
}
else
{
if (!loadandcall(argv[1]))
printEyecatchers(argv[1]);
}
return 0;
}
#else
int main(void)
{
fprintf(stderr, "This tool is not supported on this platform yet.\n");
return 1;
}
#endif /* !defined(_WRS_KERNEL) */

104
deps/paho.mqtt.c/src/Messages.c vendored Normal file
View File

@ -0,0 +1,104 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
/**
* @file
* \brief Trace messages
*
*/
#include "Messages.h"
#include "Log.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Heap.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define max_msg_len 120
static const char *protocol_message_list[] =
{
"%d %s -> CONNECT version %d clean: %d (%d)", /* 0, was 131, 68 and 69 */
"%d %s <- CONNACK rc: %d", /* 1, was 132 */
"%d %s -> CONNACK rc: %d (%d)", /* 2, was 138 */
"%d %s <- PINGREQ", /* 3, was 35 */
"%d %s -> PINGRESP (%d)", /* 4 */
"%d %s <- DISCONNECT", /* 5 */
"%d %s <- SUBSCRIBE msgid: %d", /* 6, was 39 */
"%d %s -> SUBACK msgid: %d (%d)", /* 7, was 40 */
"%d %s <- UNSUBSCRIBE msgid: %d", /* 8, was 41 */
"%d %s -> UNSUBACK msgid: %d (%d)", /* 9 */
"%d %s -> PUBLISH msgid: %d qos: %d retained: %d rc %d payload len(%d): %.*s", /* 10, was 42 */
"%d %s <- PUBLISH msgid: %d qos: %d retained: %d payload len(%d): %.*s", /* 11, was 46 */
"%d %s -> PUBACK msgid: %d (%d)", /* 12, was 47 */
"%d %s -> PUBREC msgid: %d (%d)", /* 13, was 48 */
"%d %s <- PUBACK msgid: %d", /* 14, was 49 */
"%d %s <- PUBREC msgid: %d", /* 15, was 53 */
"%d %s -> PUBREL msgid: %d (%d)", /* 16, was 57 */
"%d %s <- PUBREL msgid %d", /* 17, was 58 */
"%d %s -> PUBCOMP msgid %d (%d)", /* 18, was 62 */
"%d %s <- PUBCOMP msgid:%d", /* 19, was 63 */
"%d %s -> PINGREQ (%d)", /* 20, was 137 */
"%d %s <- PINGRESP", /* 21, was 70 */
"%d %s -> SUBSCRIBE msgid: %d (%d)", /* 22, was 72 */
"%d %s <- SUBACK msgid: %d", /* 23, was 73 */
"%d %s <- UNSUBACK msgid: %d", /* 24, was 74 */
"%d %s -> UNSUBSCRIBE msgid: %d (%d)", /* 25, was 106 */
"%d %s <- CONNECT", /* 26 */
"%d %s -> PUBLISH qos: 0 retained: %d rc: %d payload len(%d): %.*s", /* 27 */
"%d %s -> DISCONNECT (%d)", /* 28 */
"Socket error for client identifier %s, socket %d, peer address %s; ending connection", /* 29 */
};
static const char *trace_message_list[] =
{
"Failed to remove client from bstate->clients", /* 0 */
"Removed client %s from bstate->clients, socket %d", /* 1 */
"Packet_Factory: unhandled packet type %d", /* 2 */
"Packet %s received from client %s for message identifier %d, but no record of that message identifier found", /* 3 */
"Packet %s received from client %s for message identifier %d, but message is wrong QoS, %d", /* 4 */
"Packet %s received from client %s for message identifier %d, but message is in wrong state", /* 5 */
"%s received from client %s for message id %d - removing publication", /* 6 */
"Trying %s again for client %s, socket %d, message identifier %d", /* 7 */
"", /* 8 */
"(%lu) %*s(%d)> %s:%d", /* 9 */
"(%lu) %*s(%d)< %s:%d", /* 10 */
"(%lu) %*s(%d)< %s:%d (%d)", /* 11 */
"Storing unsent QoS 0 message", /* 12 */
};
/**
* Get a log message by its index
* @param index the integer index
* @param log_level the log level, used to determine which message list to use
* @return the message format string
*/
const char* Messages_get(int index, enum LOG_LEVELS log_level)
{
const char *msg = NULL;
if (log_level == TRACE_PROTOCOL)
msg = (index >= 0 && index < ARRAY_SIZE(protocol_message_list)) ? protocol_message_list[index] : NULL;
else
msg = (index >= 0 && index < ARRAY_SIZE(trace_message_list)) ? trace_message_list[index] : NULL;
return msg;
}

28
deps/paho.mqtt.c/src/OsWrapper.c vendored Normal file
View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2016, 2017 logi.cals GmbH
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Gunter Raidl - timer support for VxWorks
* Rainer Poisel - reusability
*******************************************************************************/
#include "OsWrapper.h"
#if defined(_WRS_KERNEL)
void usleep(useconds_t useconds)
{
struct timespec tv;
tv.tv_sec = useconds / 1000000;
tv.tv_nsec = (useconds % 1000000) * 1000;
nanosleep(&tv, NULL);
}
#endif /* defined(_WRS_KERNEL) */

249
deps/paho.mqtt.c/src/SHA1.c vendored Normal file
View File

@ -0,0 +1,249 @@
/*******************************************************************************
* 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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Keith Holman - initial implementation and documentation
*******************************************************************************/
#include "SHA1.h"
#if !defined(OPENSSL)
#if defined(_WIN32) || defined(_WIN64)
#pragma comment(lib, "crypt32.lib")
int SHA1_Init(SHA_CTX *c)
{
if (!CryptAcquireContext(&c->hProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
return 0;
if (!CryptCreateHash(c->hProv, CALG_SHA1, 0, 0, &c->hHash))
{
CryptReleaseContext(c->hProv, 0);
return 0;
}
return 1;
}
int SHA1_Update(SHA_CTX *c, const void *data, size_t len)
{
int rv = 1;
if (!CryptHashData(c->hHash, data, (DWORD)len, 0))
rv = 0;
return rv;
}
int SHA1_Final(unsigned char *md, SHA_CTX *c)
{
int rv = 0;
DWORD md_len = SHA1_DIGEST_LENGTH;
if (CryptGetHashParam(c->hHash, HP_HASHVAL, md, &md_len, 0))
rv = 1;
CryptDestroyHash(c->hHash);
CryptReleaseContext(c->hProv, 0);
return rv;
}
#else /* if defined(_WIN32) || defined(_WIN64) */
#if defined(__linux__) || defined(__CYGWIN__)
# include <endian.h>
#elif defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# define htobe32(x) OSSwapHostToBigInt32(x)
# define be32toh(x) OSSwapBigToHostInt32(x)
#elif defined(__FreeBSD__) || defined(__NetBSD__)
# include <sys/endian.h>
#endif
#include <string.h>
static unsigned char pad[64] = {
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int SHA1_Init(SHA_CTX *ctx)
{
int ret = 0;
if ( ctx )
{
ctx->h[0] = 0x67452301;
ctx->h[1] = 0xEFCDAB89;
ctx->h[2] = 0x98BADCFE;
ctx->h[3] = 0x10325476;
ctx->h[4] = 0xC3D2E1F0;
ctx->size = 0u;
ctx->total = 0u;
ret = 1;
}
return ret;
}
#define ROTATE_LEFT32(a, n) (((a) << (n)) | ((a) >> (32 - (n))))
static void SHA1_ProcessBlock(SHA_CTX *ctx)
{
uint32_t blks[5];
uint32_t *w;
int i;
/* initialize */
for ( i = 0; i < 5; ++i )
blks[i] = ctx->h[i];
w = ctx->w;
/* perform SHA-1 hash */
for ( i = 0; i < 16; ++i )
w[i] = be32toh(w[i]);
for( i = 0; i < 80; ++i )
{
int tmp;
if ( i >= 16 )
w[i & 0x0F] = ROTATE_LEFT32( w[(i+13) & 0x0F] ^ w[(i+8) & 0x0F] ^ w[(i+2) & 0x0F] ^ w[i & 0x0F], 1 );
if ( i < 20 )
tmp = ROTATE_LEFT32(blks[0], 5) + ((blks[1] & blks[2]) | (~(blks[1]) & blks[3])) + blks[4] + w[i & 0x0F] + 0x5A827999;
else if ( i < 40 )
tmp = ROTATE_LEFT32(blks[0], 5) + (blks[1]^blks[2]^blks[3]) + blks[4] + w[i & 0x0F] + 0x6ED9EBA1;
else if ( i < 60 )
tmp = ROTATE_LEFT32(blks[0], 5) + ((blks[1] & blks[2]) | (blks[1] & blks[3]) | (blks[2] & blks[3])) + blks[4] + w[i & 0x0F] + 0x8F1BBCDC;
else
tmp = ROTATE_LEFT32(blks[0], 5) + (blks[1]^blks[2]^blks[3]) + blks[4] + w[i & 0x0F] + 0xCA62C1D6;
/* update registers */
blks[4] = blks[3];
blks[3] = blks[2];
blks[2] = ROTATE_LEFT32(blks[1], 30);
blks[1] = blks[0];
blks[0] = tmp;
}
/* update of hash */
for ( i = 0; i < 5; ++i )
ctx->h[i] += blks[i];
}
int SHA1_Final(unsigned char *md, SHA_CTX *ctx)
{
int i;
int ret = 0;
size_t pad_amount;
uint64_t total;
/* length before pad */
total = ctx->total * 8;
if ( ctx->size < 56 )
pad_amount = 56 - ctx->size;
else
pad_amount = 64 + 56 - ctx->size;
SHA1_Update(ctx, pad, pad_amount);
ctx->w[14] = htobe32((uint32_t)(total >> 32));
ctx->w[15] = htobe32((uint32_t)total);
SHA1_ProcessBlock(ctx);
for ( i = 0; i < 5; ++i )
ctx->h[i] = htobe32(ctx->h[i]);
if ( md )
{
memcpy( md, &ctx->h[0], SHA1_DIGEST_LENGTH );
ret = 1;
}
return ret;
}
int SHA1_Update(SHA_CTX *ctx, const void *data, size_t len)
{
while ( len > 0 )
{
unsigned int n = 64 - ctx->size;
if ( len < n )
n = len;
memcpy(ctx->buffer + ctx->size, data, n);
ctx->size += n;
ctx->total += n;
data = (uint8_t *)data + n;
len -= n;
if ( ctx->size == 64 )
{
SHA1_ProcessBlock(ctx);
ctx->size = 0;
}
}
return 1;
}
#endif /* else if defined(_WIN32) || defined(_WIN64) */
#endif /* elif !defined(OPENSSL) */
#if defined(SHA1_TEST)
#include <stdio.h>
#include <string.h>
#define TEST_EXPECT(i,x) if (!(x)) {fprintf( stderr, "failed test: %s (for i == %d)\n", #x, i ); ++fails;}
int main(int argc, char *argv[])
{
struct _td
{
const char *in;
const char *out;
};
int i;
unsigned int fails = 0u;
struct _td test_data[] = {
{ "", "da39a3ee5e6b4b0d3255bfef95601890afd80709" },
{ "this string", "fda4e74bc7489a18b146abdf23346d166663dab8" },
{ NULL, NULL }
};
/* only 1 update */
i = 0;
while ( test_data[i].in != NULL )
{
int r[3] = { 1, 1, 1 };
unsigned char sha_out[SHA1_DIGEST_LENGTH];
char out[SHA1_DIGEST_LENGTH * 2 + 1];
SHA_CTX c;
int j;
r[0] = SHA1_Init( &c );
r[1] = SHA1_Update( &c, test_data[i].in, strlen(test_data[i].in));
r[2] = SHA1_Final( sha_out, &c );
for ( j = 0u; j < SHA1_DIGEST_LENGTH; ++j )
snprintf( &out[j*2], 3u, "%02x", sha_out[j] );
out[SHA1_DIGEST_LENGTH * 2] = '\0';
TEST_EXPECT( i, r[0] == 1 && r[1] == 1 && r[2] == 1 && strncmp(out, test_data[i].out, strlen(test_data[i].out)) == 0 );
++i;
}
if ( fails )
printf( "%u test failed!\n", fails );
else
printf( "all tests passed\n" );
return fails;
}
#endif /* if defined(SHA1_TEST) */

1071
deps/paho.mqtt.c/src/SSLSocket.c vendored Normal file

File diff suppressed because it is too large Load Diff

1080
deps/paho.mqtt.c/src/Socket.c vendored Normal file

File diff suppressed because it is too large Load Diff

442
deps/paho.mqtt.c/src/SocketBuffer.c vendored Normal file
View File

@ -0,0 +1,442 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
* Ian Craggs - fix for issue #244, issue #20
*******************************************************************************/
/**
* @file
* \brief Socket buffering related functions
*
* Some other related functions are in the Socket module
*/
#include "SocketBuffer.h"
#include "LinkedList.h"
#include "Log.h"
#include "Messages.h"
#include "StackTrace.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Heap.h"
#if defined(_WIN32) || defined(_WIN64)
#define iov_len len
#define iov_base buf
#endif
/**
* Default input queue buffer
*/
static socket_queue* def_queue;
/**
* List of queued input buffers
*/
static List* queues;
/**
* List of queued write buffers
*/
static List writes;
int socketcompare(void* a, void* b);
int SocketBuffer_newDefQ(void);
void SocketBuffer_freeDefQ(void);
int pending_socketcompare(void* a, void* b);
/**
* List callback function for comparing socket_queues by socket
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int socketcompare(void* a, void* b)
{
return ((socket_queue*)a)->socket == *(int*)b;
}
/**
* Create a new default queue when one has just been used.
*/
int SocketBuffer_newDefQ(void)
{
int rc = PAHO_MEMORY_ERROR;
def_queue = malloc(sizeof(socket_queue));
if (def_queue)
{
def_queue->buflen = 1000;
def_queue->buf = malloc(def_queue->buflen);
if (def_queue->buf)
{
def_queue->socket = def_queue->index = 0;
def_queue->buflen = def_queue->datalen = def_queue->headerlen = 0;
rc = 0;
}
}
return rc;
}
/**
* Initialize the socketBuffer module
*/
int SocketBuffer_initialize(void)
{
int rc = 0;
FUNC_ENTRY;
rc = SocketBuffer_newDefQ();
if (rc == 0)
{
if ((queues = ListInitialize()) == NULL)
rc = PAHO_MEMORY_ERROR;
}
ListZero(&writes);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Free the default queue memory
*/
void SocketBuffer_freeDefQ(void)
{
free(def_queue->buf);
free(def_queue);
def_queue = NULL;
}
/**
* Terminate the socketBuffer module
*/
void SocketBuffer_terminate(void)
{
ListElement* cur = NULL;
ListEmpty(&writes);
FUNC_ENTRY;
while (ListNextElement(queues, &cur))
free(((socket_queue*)(cur->content))->buf);
ListFree(queues);
SocketBuffer_freeDefQ();
FUNC_EXIT;
}
/**
* Cleanup any buffers for a specific socket
* @param socket the socket to clean up
*/
void SocketBuffer_cleanup(int socket)
{
FUNC_ENTRY;
SocketBuffer_writeComplete(socket); /* clean up write buffers */
if (ListFindItem(queues, &socket, socketcompare))
{
free(((socket_queue*)(queues->current->content))->buf);
ListRemove(queues, queues->current->content);
}
if (def_queue->socket == socket)
{
def_queue->socket = def_queue->index = 0;
def_queue->headerlen = def_queue->datalen = 0;
}
FUNC_EXIT;
}
/**
* Get any queued data for a specific socket
* @param socket the socket to get queued data for
* @param bytes the number of bytes of data to retrieve
* @param actual_len the actual length returned
* @return the actual data
*/
char* SocketBuffer_getQueuedData(int socket, size_t bytes, size_t* actual_len)
{
socket_queue* queue = NULL;
FUNC_ENTRY;
if (ListFindItem(queues, &socket, socketcompare))
{ /* if there is queued data for this socket, add any data read to it */
queue = (socket_queue*)(queues->current->content);
*actual_len = queue->datalen;
}
else
{
*actual_len = 0;
queue = def_queue;
}
if (bytes > queue->buflen)
{
if (queue->datalen > 0)
{
void* newmem = malloc(bytes);
free(queue->buf);
queue->buf = newmem;
if (!newmem)
goto exit;
memcpy(newmem, queue->buf, queue->datalen);
}
else
queue->buf = realloc(queue->buf, bytes);
queue->buflen = bytes;
}
exit:
FUNC_EXIT;
return queue->buf;
}
/**
* Get any queued character for a specific socket
* @param socket the socket to get queued data for
* @param c the character returned if any
* @return completion code
*/
int SocketBuffer_getQueuedChar(int socket, char* c)
{
int rc = SOCKETBUFFER_INTERRUPTED;
FUNC_ENTRY;
if (ListFindItem(queues, &socket, socketcompare))
{ /* if there is queued data for this socket, read that first */
socket_queue* queue = (socket_queue*)(queues->current->content);
if (queue->index < queue->headerlen)
{
*c = queue->fixed_header[(queue->index)++];
Log(TRACE_MAX, -1, "index is now %d, headerlen %d", queue->index, (int)queue->headerlen);
rc = SOCKETBUFFER_COMPLETE;
goto exit;
}
else if (queue->index > 4)
{
Log(LOG_FATAL, -1, "header is already at full length");
rc = SOCKET_ERROR;
goto exit;
}
}
exit:
FUNC_EXIT_RC(rc);
return rc; /* there was no queued char if rc is SOCKETBUFFER_INTERRUPTED*/
}
/**
* A socket read was interrupted so we need to queue data
* @param socket the socket to get queued data for
* @param actual_len the actual length of data that was read
*/
void SocketBuffer_interrupted(int socket, size_t actual_len)
{
socket_queue* queue = NULL;
FUNC_ENTRY;
if (ListFindItem(queues, &socket, socketcompare))
queue = (socket_queue*)(queues->current->content);
else /* new saved queue */
{
queue = def_queue;
/* if SocketBuffer_queueChar() has not yet been called, then the socket number
in def_queue will not have been set. Issue #244.
If actual_len == 0 then we may not need to do anything - I'll leave that
optimization for another time. */
queue->socket = socket;
ListAppend(queues, def_queue, sizeof(socket_queue)+def_queue->buflen);
SocketBuffer_newDefQ();
}
queue->index = 0;
queue->datalen = actual_len;
FUNC_EXIT;
}
/**
* A socket read has now completed so we can get rid of the queue
* @param socket the socket for which the operation is now complete
* @return pointer to the default queue data
*/
char* SocketBuffer_complete(int socket)
{
FUNC_ENTRY;
if (ListFindItem(queues, &socket, socketcompare))
{
socket_queue* queue = (socket_queue*)(queues->current->content);
SocketBuffer_freeDefQ();
def_queue = queue;
ListDetach(queues, queue);
}
def_queue->socket = def_queue->index = 0;
def_queue->headerlen = def_queue->datalen = 0;
FUNC_EXIT;
return def_queue->buf;
}
/**
* Queued a Charactor to a specific socket
* @param socket the socket for which to queue char for
* @param c the character to queue
*/
void SocketBuffer_queueChar(int socket, char c)
{
int error = 0;
socket_queue* curq = def_queue;
FUNC_ENTRY;
if (ListFindItem(queues, &socket, socketcompare))
curq = (socket_queue*)(queues->current->content);
else if (def_queue->socket == 0)
{
def_queue->socket = socket;
def_queue->index = 0;
def_queue->datalen = 0;
}
else if (def_queue->socket != socket)
{
Log(LOG_FATAL, -1, "attempt to reuse socket queue");
error = 1;
}
if (curq->index > 4)
{
Log(LOG_FATAL, -1, "socket queue fixed_header field full");
error = 1;
}
if (!error)
{
curq->fixed_header[(curq->index)++] = c;
curq->headerlen = curq->index;
}
Log(TRACE_MAX, -1, "queueChar: index is now %d, headerlen %d", curq->index, (int)curq->headerlen);
FUNC_EXIT;
}
/**
* A socket write was interrupted so store the remaining data
* @param socket the socket for which the write was interrupted
* @param count the number of iovec buffers
* @param iovecs buffer array
* @param frees a set of flags indicating which of the iovecs array should be freed
* @param total total data length to be written
* @param bytes actual data length that was written
*/
#if defined(OPENSSL)
int SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes)
#else
int SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes)
#endif
{
int i = 0;
pending_writes* pw = NULL;
int rc = 0;
FUNC_ENTRY;
/* store the buffers until the whole packet is written */
if ((pw = malloc(sizeof(pending_writes))) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
pw->socket = socket;
#if defined(OPENSSL)
pw->ssl = ssl;
#endif
pw->bytes = bytes;
pw->total = total;
pw->count = count;
for (i = 0; i < count; i++)
{
pw->iovecs[i] = iovecs[i];
pw->frees[i] = frees[i];
}
ListAppend(&writes, pw, sizeof(pw) + total);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* List callback function for comparing pending_writes by socket
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int pending_socketcompare(void* a, void* b)
{
return ((pending_writes*)a)->socket == *(int*)b;
}
/**
* Get any queued write data for a specific socket
* @param socket the socket to get queued data for
* @return pointer to the queued data or NULL
*/
pending_writes* SocketBuffer_getWrite(int socket)
{
ListElement* le = ListFindItem(&writes, &socket, pending_socketcompare);
return (le) ? (pending_writes*)(le->content) : NULL;
}
/**
* A socket write has now completed so we can get rid of the queue
* @param socket the socket for which the operation is now complete
* @return completion code, boolean - was the queue removed?
*/
int SocketBuffer_writeComplete(int socket)
{
return ListRemoveItem(&writes, &socket, pending_socketcompare);
}
/**
* Update the queued write data for a socket in the case of QoS 0 messages.
* @param socket the socket for which the operation is now complete
* @param topic the topic of the QoS 0 write
* @param payload the payload of the QoS 0 write
* @return pointer to the updated queued data structure, or NULL
*/
pending_writes* SocketBuffer_updateWrite(int socket, char* topic, char* payload)
{
pending_writes* pw = NULL;
ListElement* le = NULL;
FUNC_ENTRY;
if ((le = ListFindItem(&writes, &socket, pending_socketcompare)) != NULL)
{
pw = (pending_writes*)(le->content);
if (pw->count == 4)
{
pw->iovecs[2].iov_base = topic;
pw->iovecs[3].iov_base = payload;
}
}
FUNC_EXIT;
return pw;
}

208
deps/paho.mqtt.c/src/StackTrace.c vendored Normal file
View File

@ -0,0 +1,208 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#include "StackTrace.h"
#include "Log.h"
#include "LinkedList.h"
#include "Clients.h"
#include "Thread.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if defined(_WIN32) || defined(_WIN64)
#define snprintf _snprintf
#endif
/*BE
def STACKENTRY
{
n32 ptr STRING open "name"
n32 dec "line"
}
defList(STACKENTRY)
BE*/
#define MAX_STACK_DEPTH 50
#define MAX_FUNCTION_NAME_LENGTH 30
#define MAX_THREADS 255
typedef struct
{
thread_id_type threadid;
char name[MAX_FUNCTION_NAME_LENGTH];
int line;
} stackEntry;
typedef struct
{
thread_id_type id;
int maxdepth;
int current_depth;
stackEntry callstack[MAX_STACK_DEPTH];
} threadEntry;
#include "StackTrace.h"
#if !defined(NOSTACKTRACE)
static int thread_count = 0;
static threadEntry threads[MAX_THREADS];
static threadEntry *my_thread = NULL;
#if defined(_WIN32) || defined(_WIN64)
mutex_type stack_mutex;
#else
static pthread_mutex_t stack_mutex_store = PTHREAD_MUTEX_INITIALIZER;
static mutex_type stack_mutex = &stack_mutex_store;
#endif
int setStack(int create);
int setStack(int create)
{
int i = -1;
thread_id_type curid = Thread_getid();
my_thread = NULL;
for (i = 0; i < MAX_THREADS && i < thread_count; ++i)
{
if (threads[i].id == curid)
{
my_thread = &threads[i];
break;
}
}
if (my_thread == NULL && create && thread_count < MAX_THREADS)
{
my_thread = &threads[thread_count];
my_thread->id = curid;
my_thread->maxdepth = 0;
my_thread->current_depth = 0;
++thread_count;
}
return my_thread != NULL; /* good == 1 */
}
void StackTrace_entry(const char* name, int line, enum LOG_LEVELS trace_level)
{
Thread_lock_mutex(stack_mutex);
if (!setStack(1))
goto exit;
if (trace_level != -1)
Log_stackTrace(trace_level, 9, (int)my_thread->id, my_thread->current_depth, name, line, NULL);
strncpy(my_thread->callstack[my_thread->current_depth].name, name, sizeof(my_thread->callstack[0].name)-1);
my_thread->callstack[(my_thread->current_depth)++].line = line;
if (my_thread->current_depth > my_thread->maxdepth)
my_thread->maxdepth = my_thread->current_depth;
if (my_thread->current_depth >= MAX_STACK_DEPTH)
Log(LOG_FATAL, -1, "Max stack depth exceeded");
exit:
Thread_unlock_mutex(stack_mutex);
}
void StackTrace_exit(const char* name, int line, void* rc, enum LOG_LEVELS trace_level)
{
Thread_lock_mutex(stack_mutex);
if (!setStack(0))
goto exit;
if (--(my_thread->current_depth) < 0)
Log(LOG_FATAL, -1, "Minimum stack depth exceeded for thread %lu", my_thread->id);
if (strncmp(my_thread->callstack[my_thread->current_depth].name, name, sizeof(my_thread->callstack[0].name)-1) != 0)
Log(LOG_FATAL, -1, "Stack mismatch. Entry:%s Exit:%s\n", my_thread->callstack[my_thread->current_depth].name, name);
if (trace_level != -1)
{
if (rc == NULL)
Log_stackTrace(trace_level, 10, (int)my_thread->id, my_thread->current_depth, name, line, NULL);
else
Log_stackTrace(trace_level, 11, (int)my_thread->id, my_thread->current_depth, name, line, (int*)rc);
}
exit:
Thread_unlock_mutex(stack_mutex);
}
void StackTrace_printStack(FILE* dest)
{
FILE* file = stdout;
int t = 0;
if (dest)
file = dest;
for (t = 0; t < thread_count; ++t)
{
threadEntry *cur_thread = &threads[t];
if (cur_thread->id > 0)
{
int i = cur_thread->current_depth - 1;
fprintf(file, "=========== Start of stack trace for thread %lu ==========\n", (unsigned long)cur_thread->id);
if (i >= 0)
{
fprintf(file, "%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
while (--i >= 0)
fprintf(file, " at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
}
fprintf(file, "=========== End of stack trace for thread %lu ==========\n\n", (unsigned long)cur_thread->id);
}
}
if (file != stdout && file != stderr && file != NULL)
fclose(file);
}
char* StackTrace_get(thread_id_type threadid, char* buf, int bufsize)
{
int t = 0;
if (bufsize < 100)
goto exit;
buf[0] = '\0';
for (t = 0; t < thread_count; ++t)
{
threadEntry *cur_thread = &threads[t];
if (cur_thread->id == threadid)
{
int i = cur_thread->current_depth - 1;
int curpos = 0;
if (i >= 0)
{
curpos += snprintf(&buf[curpos], bufsize - curpos -1,
"%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
while (--i >= 0)
curpos += snprintf(&buf[curpos], bufsize - curpos -1, /* lgtm [cpp/overflowing-snprintf] */
" at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
if (buf[--curpos] == '\n')
buf[curpos] = '\0';
}
break;
}
}
exit:
return buf;
}
#endif

649
deps/paho.mqtt.c/src/Thread.c vendored Normal file
View File

@ -0,0 +1,649 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial implementation
* Ian Craggs, Allan Stockdill-Mander - async client updates
* Ian Craggs - bug #415042 - start Linux thread as disconnected
* Ian Craggs - fix for bug #420851
* Ian Craggs - change MacOS semaphore implementation
* Ian Craggs - fix for clock #284
*******************************************************************************/
/**
* @file
* \brief Threading related functions
*
* Used to create platform independent threading functions
*/
#include "Thread.h"
#if defined(THREAD_UNIT_TESTS)
#define NOSTACKTRACE
#endif
#include "Log.h"
#include "StackTrace.h"
#undef malloc
#undef realloc
#undef free
#if !defined(_WIN32) && !defined(_WIN64)
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <limits.h>
#endif
#include <stdlib.h>
#include "OsWrapper.h"
/**
* Start a new thread
* @param fn the function to run, must be of the correct signature
* @param parameter pointer to the function parameter, can be NULL
* @return the new thread
*/
thread_type Thread_start(thread_fn fn, void* parameter)
{
#if defined(_WIN32) || defined(_WIN64)
thread_type thread = NULL;
#else
thread_type thread = 0;
pthread_attr_t attr;
#endif
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
thread = CreateThread(NULL, 0, fn, parameter, 0, NULL);
#else
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (pthread_create(&thread, &attr, fn, parameter) != 0)
thread = 0;
pthread_attr_destroy(&attr);
#endif
FUNC_EXIT;
return thread;
}
/**
* Create a new mutex
* @return the new mutex
*/
mutex_type Thread_create_mutex(int* rc)
{
mutex_type mutex = NULL;
FUNC_ENTRY;
*rc = -1;
#if defined(_WIN32) || defined(_WIN64)
mutex = CreateMutex(NULL, 0, NULL);
if (mutex == NULL)
*rc = GetLastError();
#else
mutex = malloc(sizeof(pthread_mutex_t));
if (mutex)
*rc = pthread_mutex_init(mutex, NULL);
#endif
FUNC_EXIT_RC(*rc);
return mutex;
}
/**
* Lock a mutex which has alrea
* @return completion code, 0 is success
*/
int Thread_lock_mutex(mutex_type mutex)
{
int rc = -1;
/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
#if defined(_WIN32) || defined(_WIN64)
/* WaitForSingleObject returns WAIT_OBJECT_0 (0), on success */
rc = WaitForSingleObject(mutex, INFINITE);
#else
rc = pthread_mutex_lock(mutex);
#endif
return rc;
}
/**
* Unlock a mutex which has already been locked
* @param mutex the mutex
* @return completion code, 0 is success
*/
int Thread_unlock_mutex(mutex_type mutex)
{
int rc = -1;
/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
#if defined(_WIN32) || defined(_WIN64)
/* if ReleaseMutex fails, the return value is 0 */
if (ReleaseMutex(mutex) == 0)
rc = GetLastError();
else
rc = 0;
#else
rc = pthread_mutex_unlock(mutex);
#endif
return rc;
}
/**
* Destroy a mutex which has already been created
* @param mutex the mutex
*/
int Thread_destroy_mutex(mutex_type mutex)
{
int rc = 0;
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
rc = CloseHandle(mutex);
#else
rc = pthread_mutex_destroy(mutex);
free(mutex);
#endif
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Get the thread id of the thread from which this function is called
* @return thread id, type varying according to OS
*/
thread_id_type Thread_getid(void)
{
#if defined(_WIN32) || defined(_WIN64)
return GetCurrentThreadId();
#else
return pthread_self();
#endif
}
/**
* Create a new semaphore
* @return the new condition variable
*/
sem_type Thread_create_sem(int *rc)
{
sem_type sem = NULL;
FUNC_ENTRY;
*rc = -1;
#if defined(_WIN32) || defined(_WIN64)
sem = CreateEvent(
NULL, /* default security attributes */
FALSE, /* manual-reset event? */
FALSE, /* initial state is nonsignaled */
NULL /* object name */
);
#if 0
sem = CreateSemaphore(
NULL, /* default security attributes */
0, /* initial count - non signaled */
1, /* maximum count */
NULL /* unnamed semaphore */
);
#endif
#elif defined(OSX)
sem = dispatch_semaphore_create(0L);
*rc = (sem == NULL) ? -1 : 0;
#else
sem = malloc(sizeof(sem_t));
if (sem)
*rc = sem_init(sem, 0, 0);
#endif
FUNC_EXIT_RC(*rc);
return sem;
}
/**
* Wait for a semaphore to be posted, or timeout.
* @param sem the semaphore
* @param timeout the maximum time to wait, in milliseconds
* @return completion code
*/
int Thread_wait_sem(sem_type sem, int timeout)
{
/* sem_timedwait is the obvious call to use, but seemed not to work on the Viper,
* so I've used trywait in a loop instead. Ian Craggs 23/7/2010
*/
int rc = -1;
#if !defined(_WIN32) && !defined(_WIN64) && !defined(OSX)
#define USE_TRYWAIT
#if defined(USE_TRYWAIT)
int i = 0;
useconds_t interval = 10000; /* 10000 microseconds: 10 milliseconds */
int count = (1000 * timeout) / interval; /* how many intervals in timeout period */
#else
struct timespec ts;
#endif
#endif
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
/* returns 0 (WAIT_OBJECT_0) on success, non-zero (WAIT_TIMEOUT) if timeout occurred */
rc = WaitForSingleObject(sem, timeout < 0 ? 0 : timeout);
if (rc == WAIT_TIMEOUT)
rc = ETIMEDOUT;
#elif defined(OSX)
/* returns 0 on success, non-zero if timeout occurred */
rc = (int)dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout*1000000L));
if (rc != 0)
rc = ETIMEDOUT;
#elif defined(USE_TRYWAIT)
while (++i < count && (rc = sem_trywait(sem)) != 0)
{
if (rc == -1 && ((rc = errno) != EAGAIN))
{
rc = 0;
break;
}
usleep(interval); /* microseconds - .1 of a second */
}
#else
/* We have to use CLOCK_REALTIME rather than MONOTONIC for sem_timedwait interval.
* Does this make it susceptible to system clock changes?
* The intervals are small enough, and repeated, that I think it's not an issue.
*/
if (clock_gettime(CLOCK_REALTIME, &ts) != -1)
{
ts.tv_sec += timeout;
rc = sem_timedwait(sem, &ts);
}
#endif
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Check to see if a semaphore has been posted, without waiting
* The semaphore will be unchanged, if the return value is false.
* The semaphore will have been decremented, if the return value is true.
* @param sem the semaphore
* @return 0 (false) or 1 (true)
*/
int Thread_check_sem(sem_type sem)
{
#if defined(_WIN32) || defined(_WIN64)
/* if the return value is not 0, the semaphore will not have been decremented */
return WaitForSingleObject(sem, 0) == WAIT_OBJECT_0;
#elif defined(OSX)
/* if the return value is not 0, the semaphore will not have been decremented */
return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
#else
/* If the call was unsuccessful, the state of the semaphore shall be unchanged,
* and the function shall return a value of -1 */
return sem_trywait(sem) == 0;
#endif
}
/**
* Post a semaphore
* @param sem the semaphore
* @return 0 on success
*/
int Thread_post_sem(sem_type sem)
{
int rc = 0;
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
if (SetEvent(sem) == 0)
rc = GetLastError();
#elif defined(OSX)
rc = (int)dispatch_semaphore_signal(sem);
#else
int val;
int rc1 = sem_getvalue(sem, &val);
if (rc1 != 0)
rc = errno;
else if (val == 0 && sem_post(sem) == -1)
rc = errno;
#endif
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Destroy a semaphore which has already been created
* @param sem the semaphore
*/
int Thread_destroy_sem(sem_type sem)
{
int rc = 0;
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
rc = CloseHandle(sem);
#elif defined(OSX)
dispatch_release(sem);
#else
rc = sem_destroy(sem);
free(sem);
#endif
FUNC_EXIT_RC(rc);
return rc;
}
#if !defined(_WIN32) && !defined(_WIN64)
/**
* Create a new condition variable
* @return the condition variable struct
*/
cond_type Thread_create_cond(int *rc)
{
cond_type condvar = NULL;
pthread_condattr_t attr;
FUNC_ENTRY;
*rc = -1;
pthread_condattr_init(&attr);
#if 0
/* in theory, a monotonic clock should be able to be used. However on at least
* one system reported, even though setclock() reported success, it didn't work.
*/
if ((rc = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) == 0)
use_clock_monotonic = 1;
else
Log(LOG_ERROR, -1, "Error %d calling pthread_condattr_setclock(CLOCK_MONOTONIC)", rc);
#endif
condvar = malloc(sizeof(cond_type_struct));
if (condvar)
{
*rc = pthread_cond_init(&condvar->cond, &attr);
*rc = pthread_mutex_init(&condvar->mutex, NULL);
}
FUNC_EXIT_RC(*rc);
return condvar;
}
/**
* Signal a condition variable
* @return completion code
*/
int Thread_signal_cond(cond_type condvar)
{
int rc = 0;
FUNC_ENTRY;
pthread_mutex_lock(&condvar->mutex);
rc = pthread_cond_signal(&condvar->cond);
pthread_mutex_unlock(&condvar->mutex);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Wait with a timeout (seconds) for condition variable
* @return 0 for success, ETIMEDOUT otherwise
*/
int Thread_wait_cond(cond_type condvar, int timeout)
{
int rc = 0;
struct timespec cond_timeout;
FUNC_ENTRY;
clock_gettime(CLOCK_REALTIME, &cond_timeout);
cond_timeout.tv_sec += timeout;
pthread_mutex_lock(&condvar->mutex);
rc = pthread_cond_timedwait(&condvar->cond, &condvar->mutex, &cond_timeout);
pthread_mutex_unlock(&condvar->mutex);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Destroy a condition variable
* @return completion code
*/
int Thread_destroy_cond(cond_type condvar)
{
int rc = 0;
rc = pthread_mutex_destroy(&condvar->mutex);
rc = pthread_cond_destroy(&condvar->cond);
free(condvar);
return rc;
}
#endif
#if defined(THREAD_UNIT_TESTS)
#if defined(_WIN32) || defined(_WINDOWS)
#define mqsleep(A) Sleep(1000*A)
#define START_TIME_TYPE DWORD
static DWORD start_time = 0;
START_TIME_TYPE start_clock(void)
{
return GetTickCount();
}
#elif defined(AIX)
#define mqsleep sleep
#define START_TIME_TYPE struct timespec
START_TIME_TYPE start_clock(void)
{
static struct timespec start;
clock_gettime(CLOCK_REALTIME, &start);
return start;
}
#else
#define mqsleep sleep
#define START_TIME_TYPE struct timeval
/* TODO - unused - remove? static struct timeval start_time; */
START_TIME_TYPE start_clock(void)
{
struct timeval start_time;
gettimeofday(&start_time, NULL);
return start_time;
}
#endif
#if defined(_WIN32)
long elapsed(START_TIME_TYPE start_time)
{
return GetTickCount() - start_time;
}
#elif defined(AIX)
#define assert(a)
long elapsed(struct timespec start)
{
struct timespec now, res;
clock_gettime(CLOCK_REALTIME, &now);
ntimersub(now, start, res);
return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L;
}
#else
long elapsed(START_TIME_TYPE start_time)
{
struct timeval now, res;
gettimeofday(&now, NULL);
timersub(&now, &start_time, &res);
return (res.tv_sec)*1000 + (res.tv_usec)/1000;
}
#endif
int tests = 0, failures = 0;
void myassert(char* filename, int lineno, char* description, int value, char* format, ...)
{
++tests;
if (!value)
{
va_list args;
++failures;
printf("Assertion failed, file %s, line %d, description: %s\n", filename, lineno, description);
va_start(args, format);
vprintf(format, args);
va_end(args);
//cur_output += sprintf(cur_output, "<failure type=\"%s\">file %s, line %d </failure>\n",
// description, filename, lineno);
}
else
printf("Assertion succeeded, file %s, line %d, description: %s\n", filename, lineno, description);
}
#define assert(a, b, c, d) myassert(__FILE__, __LINE__, a, b, c, d)
#define assert1(a, b, c, d, e) myassert(__FILE__, __LINE__, a, b, c, d, e)
#include <stdio.h>
thread_return_type cond_secondary(void* n)
{
int rc = 0;
cond_type cond = n;
printf("This should return immediately as it was posted already\n");
rc = Thread_wait_cond(cond, 99999);
assert("rc 1 from wait_cond", rc == 1, "rc was %d", rc);
printf("This should hang around a few seconds\n");
rc = Thread_wait_cond(cond, 99999);
assert("rc 1 from wait_cond", rc == 1, "rc was %d", rc);
printf("Secondary cond thread ending\n");
return 0;
}
int cond_test()
{
int rc = 0;
cond_type cond = Thread_create_cond();
thread_type thread;
printf("Post secondary so it should return immediately\n");
rc = Thread_signal_cond(cond);
assert("rc 0 from signal cond", rc == 0, "rc was %d", rc);
printf("Starting secondary thread\n");
thread = Thread_start(cond_secondary, (void*)cond);
sleep(3);
printf("post secondary\n");
rc = Thread_signal_cond(cond);
assert("rc 1 from signal cond", rc == 1, "rc was %d", rc);
sleep(3);
printf("Main thread ending\n");
return failures;
}
thread_return_type sem_secondary(void* n)
{
int rc = 0;
sem_type sem = n;
printf("Secondary semaphore pointer %p\n", sem);
rc = Thread_check_sem(sem);
assert("rc 1 from check_sem", rc == 1, "rc was %d", rc);
printf("Secondary thread about to wait\n");
rc = Thread_wait_sem(sem, 99999);
printf("Secondary thread returned from wait %d\n", rc);
printf("Secondary thread about to wait\n");
rc = Thread_wait_sem(sem, 99999);
printf("Secondary thread returned from wait %d\n", rc);
printf("Secondary check sem %d\n", Thread_check_sem(sem));
printf("Secondary thread ending\n");
return 0;
}
int sem_test()
{
int rc = 0;
sem_type sem = Thread_create_sem();
thread_type thread;
printf("Primary semaphore pointer %p\n", sem);
rc = Thread_check_sem(sem);
assert("rc 0 from check_sem", rc == 0, "rc was %d\n", rc);
printf("post secondary so then check should be 1\n");
rc = Thread_post_sem(sem);
assert("rc 0 from post_sem", rc == 0, "rc was %d\n", rc);
rc = Thread_check_sem(sem);
assert("rc 1 from check_sem", rc == 1, "rc was %d", rc);
printf("Starting secondary thread\n");
thread = Thread_start(sem_secondary, (void*)sem);
sleep(3);
rc = Thread_check_sem(sem);
assert("rc 1 from check_sem", rc == 1, "rc was %d", rc);
printf("post secondary\n");
rc = Thread_post_sem(sem);
assert("rc 1 from post_sem", rc == 1, "rc was %d", rc);
sleep(3);
printf("Main thread ending\n");
return failures;
}
int main(int argc, char *argv[])
{
sem_test();
//cond_test();
}
#endif

729
deps/paho.mqtt.c/src/Tree.c vendored Normal file
View File

@ -0,0 +1,729 @@
/*******************************************************************************
* Copyright (c) 2009, 2020 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial implementation and documentation
*******************************************************************************/
/** @file
* \brief functions which apply to tree structures.
*
* These trees can hold data of any sort, pointed to by the content pointer of the
* Node structure.
* */
#define TREE_C /* so that malloc/free/realloc aren't redefined by Heap.h */
#include "Tree.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Heap.h"
int isRed(Node* aNode);
int isBlack(Node* aNode);
/*int TreeWalk(Node* curnode, int depth);*/
/*int TreeMaxDepth(Tree *aTree);*/
void TreeRotate(Tree* aTree, Node* curnode, int direction, int index);
Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index);
void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index);
void* TreeAddByIndex(Tree* aTree, void* content, size_t size, int index);
Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value);
Node* TreeFindContentIndex(Tree* aTree, void* key, int index);
Node* TreeMinimum(Node* curnode);
Node* TreeSuccessor(Node* curnode);
Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index);
Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index);
void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index);
void* TreeRemoveIndex(Tree* aTree, void* content, int index);
void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int))
{
memset(aTree, '\0', sizeof(Tree));
aTree->heap_tracking = 1;
aTree->index[0].compare = compare;
aTree->indexes = 1;
}
/**
* Allocates and initializes a new tree structure.
* @return a pointer to the new tree structure
*/
Tree* TreeInitialize(int(*compare)(void*, void*, int))
{
#if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING)
Tree* newt = malloc(sizeof(Tree));
#else
Tree* newt = mymalloc(__FILE__, __LINE__, sizeof(Tree));
#endif
if (newt)
TreeInitializeNoMalloc(newt, compare);
return newt;
}
void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int))
{
aTree->index[aTree->indexes].compare = compare;
++(aTree->indexes);
}
void TreeFree(Tree* aTree)
{
#if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING)
free(aTree);
#else
(aTree->heap_tracking) ? myfree(__FILE__, __LINE__, aTree) : free(aTree);
#endif
}
#define LEFT 0
#define RIGHT 1
#if !defined(max)
#define max(a, b) (a > b) ? a : b;
#endif
int isRed(Node* aNode)
{
return (aNode != NULL) && (aNode->red);
}
int isBlack(Node* aNode)
{
return (aNode == NULL) || (aNode->red == 0);
}
#if 0
int TreeWalk(Node* curnode, int depth)
{
if (curnode)
{
int left = TreeWalk(curnode->child[LEFT], depth+1);
int right = TreeWalk(curnode->child[RIGHT], depth+1);
depth = max(left, right);
if (curnode->red)
{
/*if (isRed(curnode->child[LEFT]) || isRed(curnode->child[RIGHT]))
{
printf("red/black tree violation %p\n", curnode->content);
exit(-99);
}*/;
}
}
return depth;
}
int TreeMaxDepth(Tree *aTree)
{
int rc = TreeWalk(aTree->index[0].root, 0);
/*if (aTree->root->red)
{
printf("root node should not be red %p\n", aTree->root->content);
exit(-99);
}*/
return rc;
}
#endif
void TreeRotate(Tree* aTree, Node* curnode, int direction, int index)
{
Node* other = curnode->child[!direction];
curnode->child[!direction] = other->child[direction];
if (other->child[direction] != NULL)
other->child[direction]->parent = curnode;
other->parent = curnode->parent;
if (curnode->parent == NULL)
aTree->index[index].root = other;
else if (curnode == curnode->parent->child[direction])
curnode->parent->child[direction] = other;
else
curnode->parent->child[!direction] = other;
other->child[direction] = curnode;
curnode->parent = other;
}
Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index)
{
Node* uncle = curnode->parent->parent->child[which];
if (isRed(uncle))
{
curnode->parent->red = uncle->red = 0;
curnode = curnode->parent->parent;
curnode->red = 1;
}
else
{
if (curnode == curnode->parent->child[which])
{
curnode = curnode->parent;
TreeRotate(aTree, curnode, !which, index);
}
curnode->parent->red = 0;
curnode->parent->parent->red = 1;
TreeRotate(aTree, curnode->parent->parent, which, index);
}
return curnode;
}
void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index)
{
while (curnode && isRed(curnode->parent) && curnode->parent->parent)
{
if (curnode->parent == curnode->parent->parent->child[LEFT])
curnode = TreeBAASub(aTree, curnode, RIGHT, index);
else
curnode = TreeBAASub(aTree, curnode, LEFT, index);
}
aTree->index[index].root->red = 0;
}
/**
* Add an item to a tree
* @param aTree the list to which the item is to be added
* @param content the list item content itself
* @param size the size of the element
*/
void* TreeAddByIndex(Tree* aTree, void* content, size_t size, int index)
{
Node* curparent = NULL;
Node* curnode = aTree->index[index].root;
Node* newel = NULL;
int left = 0;
int result = 1;
void* rc = NULL;
while (curnode)
{
result = aTree->index[index].compare(curnode->content, content, 1);
left = (result > 0);
if (result == 0)
break;
else
{
curparent = curnode;
curnode = curnode->child[left];
}
}
if (result == 0)
{
if (aTree->allow_duplicates)
goto exit; /* exit(-99); */
else
{
newel = curnode;
if (index == 0)
aTree->size += (size - curnode->size);
}
}
else
{
#if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING)
newel = malloc(sizeof(Node));
#else
newel = (aTree->heap_tracking) ? mymalloc(__FILE__, __LINE__, sizeof(Node)) : malloc(sizeof(Node));
#endif
if (newel == NULL)
goto exit;
memset(newel, '\0', sizeof(Node));
if (curparent)
curparent->child[left] = newel;
else
aTree->index[index].root = newel;
newel->parent = curparent;
newel->red = 1;
if (index == 0)
{
++(aTree->count);
aTree->size += size;
}
}
newel->content = content;
newel->size = size;
rc = newel->content;
TreeBalanceAfterAdd(aTree, newel, index);
exit:
return rc;
}
void* TreeAdd(Tree* aTree, void* content, size_t size)
{
void* rc = NULL;
int i;
for (i = 0; i < aTree->indexes; ++i)
rc = TreeAddByIndex(aTree, content, size, i);
return rc;
}
Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value)
{
int result = 0;
Node* curnode = aTree->index[index].root;
while (curnode)
{
result = aTree->index[index].compare(curnode->content, key, value);
if (result == 0)
break;
else
curnode = curnode->child[result > 0];
}
return curnode;
}
Node* TreeFindIndex(Tree* aTree, void* key, int index)
{
return TreeFindIndex1(aTree, key, index, 0);
}
Node* TreeFindContentIndex(Tree* aTree, void* key, int index)
{
return TreeFindIndex1(aTree, key, index, 1);
}
Node* TreeFind(Tree* aTree, void* key)
{
return TreeFindIndex(aTree, key, 0);
}
Node* TreeMinimum(Node* curnode)
{
if (curnode)
while (curnode->child[LEFT])
curnode = curnode->child[LEFT];
return curnode;
}
Node* TreeSuccessor(Node* curnode)
{
if (curnode->child[RIGHT])
curnode = TreeMinimum(curnode->child[RIGHT]);
else
{
Node* curparent = curnode->parent;
while (curparent && curnode == curparent->child[RIGHT])
{
curnode = curparent;
curparent = curparent->parent;
}
curnode = curparent;
}
return curnode;
}
Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index)
{
if (curnode == NULL)
curnode = TreeMinimum(aTree->index[index].root);
else
curnode = TreeSuccessor(curnode);
return curnode;
}
Node* TreeNextElement(Tree* aTree, Node* curnode)
{
return TreeNextElementIndex(aTree, curnode, 0);
}
Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index)
{
Node* sibling = curnode->parent->child[which];
if (isRed(sibling))
{
sibling->red = 0;
curnode->parent->red = 1;
TreeRotate(aTree, curnode->parent, !which, index);
sibling = curnode->parent->child[which];
}
if (!sibling)
curnode = curnode->parent;
else if (isBlack(sibling->child[!which]) && isBlack(sibling->child[which]))
{
sibling->red = 1;
curnode = curnode->parent;
}
else
{
if (isBlack(sibling->child[which]))
{
sibling->child[!which]->red = 0;
sibling->red = 1;
TreeRotate(aTree, sibling, which, index);
sibling = curnode->parent->child[which];
}
sibling->red = curnode->parent->red;
curnode->parent->red = 0;
sibling->child[which]->red = 0;
TreeRotate(aTree, curnode->parent, !which, index);
curnode = aTree->index[index].root;
}
return curnode;
}
void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index)
{
while (curnode != aTree->index[index].root && isBlack(curnode))
{
/* curnode->content == NULL must equal curnode == NULL */
if (((curnode->content) ? curnode : NULL) == curnode->parent->child[LEFT])
curnode = TreeBARSub(aTree, curnode, RIGHT, index);
else
curnode = TreeBARSub(aTree, curnode, LEFT, index);
}
curnode->red = 0;
}
/**
* Remove an item from a tree
* @param aTree the list to which the item is to be added
* @param curnode the list item content itself
*/
void* TreeRemoveNodeIndex(Tree* aTree, Node* curnode, int index)
{
Node* redundant = curnode;
Node* curchild = NULL;
size_t size = curnode->size;
void* content = curnode->content;
/* if the node to remove has 0 or 1 children, it can be removed without involving another node */
if (curnode->child[LEFT] && curnode->child[RIGHT]) /* 2 children */
redundant = TreeSuccessor(curnode); /* now redundant must have at most one child */
curchild = redundant->child[(redundant->child[LEFT] != NULL) ? LEFT : RIGHT];
if (curchild) /* we could have no children at all */
curchild->parent = redundant->parent;
if (redundant->parent == NULL)
aTree->index[index].root = curchild;
else
{
if (redundant == redundant->parent->child[LEFT])
redundant->parent->child[LEFT] = curchild;
else
redundant->parent->child[RIGHT] = curchild;
}
if (redundant != curnode)
{
curnode->content = redundant->content;
curnode->size = redundant->size;
}
if (isBlack(redundant))
{
if (curchild == NULL)
{
if (redundant->parent)
{
Node temp;
memset(&temp, '\0', sizeof(Node));
temp.parent = redundant->parent;
temp.red = 0;
TreeBalanceAfterRemove(aTree, &temp, index);
}
}
else
TreeBalanceAfterRemove(aTree, curchild, index);
}
#if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING)
free(redundant);
#else
(aTree->heap_tracking) ? myfree(__FILE__, __LINE__, redundant) : free(redundant);
#endif
if (index == 0)
{
aTree->size -= size;
--(aTree->count);
}
return content;
}
/**
* Remove an item from a tree
* @param aTree the list to which the item is to be added
* @param curnode the list item content itself
*/
void* TreeRemoveIndex(Tree* aTree, void* content, int index)
{
Node* curnode = TreeFindContentIndex(aTree, content, index);
if (curnode == NULL)
return NULL;
return TreeRemoveNodeIndex(aTree, curnode, index);
}
void* TreeRemove(Tree* aTree, void* content)
{
int i;
void* rc = NULL;
for (i = 0; i < aTree->indexes; ++i)
rc = TreeRemoveIndex(aTree, content, i);
return rc;
}
void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index)
{
Node* curnode = TreeFindIndex(aTree, key, index);
void* content = NULL;
int i;
if (curnode == NULL)
return NULL;
content = TreeRemoveNodeIndex(aTree, curnode, index);
for (i = 0; i < aTree->indexes; ++i)
{
if (i != index)
content = TreeRemoveIndex(aTree, content, i);
}
return content;
}
void* TreeRemoveKey(Tree* aTree, void* key)
{
return TreeRemoveKeyIndex(aTree, key, 0);
}
int TreeIntCompare(void* a, void* b, int content)
{
int i = *((int*)a);
int j = *((int*)b);
/* printf("comparing %d %d\n", *((int*)a), *((int*)b)); */
return (i > j) ? -1 : (i == j) ? 0 : 1;
}
int TreePtrCompare(void* a, void* b, int content)
{
return (a > b) ? -1 : (a == b) ? 0 : 1;
}
int TreeStringCompare(void* a, void* b, int content)
{
return strcmp((char*)a, (char*)b);
}
#if defined(UNIT_TESTS)
int check(Tree *t)
{
Node* curnode = NULL;
int rc = 0;
curnode = TreeNextElement(t, curnode);
while (curnode)
{
Node* prevnode = curnode;
curnode = TreeNextElement(t, curnode);
if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
{
printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
rc = 99;
}
}
return rc;
}
int traverse(Tree *t, int lookfor)
{
Node* curnode = NULL;
int rc = 0;
printf("Traversing\n");
curnode = TreeNextElement(t, curnode);
/* printf("content int %d\n", *(int*)(curnode->content)); */
while (curnode)
{
Node* prevnode = curnode;
curnode = TreeNextElement(t, curnode);
/* if (curnode)
printf("content int %d\n", *(int*)(curnode->content)); */
if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
{
printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
}
if (curnode && (lookfor == *(int*)(curnode->content)))
printf("missing item %d actually found\n", lookfor);
}
printf("End traverse %d\n", rc);
return rc;
}
int test(int limit)
{
int i, *ip, *todelete;
Node* current = NULL;
Tree* t = TreeInitialize(TreeIntCompare);
int rc = 0;
printf("Tree initialized\n");
srand(time(NULL));
ip = malloc(sizeof(int));
*ip = 2;
TreeAdd(t, (void*)ip, sizeof(int));
check(t);
i = 2;
void* result = TreeRemove(t, (void*)&i);
if (result)
free(result);
int actual[limit];
for (i = 0; i < limit; i++)
{
void* replaced = NULL;
ip = malloc(sizeof(int));
*ip = rand();
replaced = TreeAdd(t, (void*)ip, sizeof(int));
if (replaced) /* duplicate */
{
free(replaced);
actual[i] = -1;
}
else
actual[i] = *ip;
if (i==5)
todelete = ip;
printf("Tree element added %d\n", *ip);
if (1 % 1000 == 0)
{
rc = check(t);
printf("%d elements, check result %d\n", i+1, rc);
if (rc != 0)
return 88;
}
}
check(t);
for (i = 0; i < limit; i++)
{
int parm = actual[i];
if (parm == -1)
continue;
Node* found = TreeFind(t, (void*)&parm);
if (found)
printf("Tree find %d %d\n", parm, *(int*)(found->content));
else
{
printf("%d not found\n", parm);
traverse(t, parm);
return -2;
}
}
check(t);
for (i = limit -1; i >= 0; i--)
{
int parm = actual[i];
void *found;
if (parm == -1) /* skip duplicate */
continue;
found = TreeRemove(t, (void*)&parm);
if (found)
{
printf("%d Tree remove %d %d\n", i, parm, *(int*)(found));
free(found);
}
else
{
int count = 0;
printf("%d %d not found\n", i, parm);
traverse(t, parm);
for (i = 0; i < limit; i++)
if (actual[i] == parm)
++count;
printf("%d occurs %d times\n", parm, count);
return -2;
}
if (i % 1000 == 0)
{
rc = check(t);
printf("%d elements, check result %d\n", i+1, rc);
if (rc != 0)
return 88;
}
}
printf("finished\n");
return 0;
}
int main(int argc, char *argv[])
{
int rc = 0;
while (rc == 0)
rc = test(999999);
}
#endif

1466
deps/paho.mqtt.c/src/WebSocket.c vendored Normal file

File diff suppressed because it is too large Load Diff

239
deps/paho.mqtt.c/src/utf-8.c vendored Normal file
View File

@ -0,0 +1,239 @@
/*******************************************************************************
* 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 v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
/**
* @file
* \brief Functions for checking that strings contain UTF-8 characters only
*
* See page 104 of the Unicode Standard 5.0 for the list of well formed
* UTF-8 byte sequences.
*
*/
#include "utf-8.h"
#include <stdlib.h>
#include <string.h>
#include "StackTrace.h"
/**
* Macro to determine the number of elements in a single-dimension array
*/
#if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
/**
* Structure to hold the valid ranges of UTF-8 characters, for each byte up to 4
*/
struct
{
int len; /**< number of elements in the following array (1 to 4) */
struct
{
char lower; /**< lower limit of valid range */
char upper; /**< upper limit of valid range */
} bytes[4]; /**< up to 4 bytes can be used per character */
}
valid_ranges[] =
{
{1, { {00, 0x7F} } },
{2, { {0xC2, 0xDF}, {0x80, 0xBF} } },
{3, { {0xE0, 0xE0}, {0xA0, 0xBF}, {0x80, 0xBF} } },
{3, { {0xE1, 0xEC}, {0x80, 0xBF}, {0x80, 0xBF} } },
{3, { {0xED, 0xED}, {0x80, 0x9F}, {0x80, 0xBF} } },
{3, { {0xEE, 0xEF}, {0x80, 0xBF}, {0x80, 0xBF} } },
{4, { {0xF0, 0xF0}, {0x90, 0xBF}, {0x80, 0xBF}, {0x80, 0xBF} } },
{4, { {0xF1, 0xF3}, {0x80, 0xBF}, {0x80, 0xBF}, {0x80, 0xBF} } },
{4, { {0xF4, 0xF4}, {0x80, 0x8F}, {0x80, 0xBF}, {0x80, 0xBF} } },
};
static const char* UTF8_char_validate(int len, const char* data);
/**
* Validate a single UTF-8 character
* @param len the length of the string in "data"
* @param data the bytes to check for a valid UTF-8 char
* @return pointer to the start of the next UTF-8 character in "data"
*/
static const char* UTF8_char_validate(int len, const char* data)
{
int good = 0;
int charlen = 2;
int i, j;
const char *rc = NULL;
if (data == NULL)
goto exit; /* don't have data, can't continue */
/* first work out how many bytes this char is encoded in */
if ((data[0] & 128) == 0)
charlen = 1;
else if ((data[0] & 0xF0) == 0xF0)
charlen = 4;
else if ((data[0] & 0xE0) == 0xE0)
charlen = 3;
if (charlen > len)
goto exit; /* not enough characters in the string we were given */
for (i = 0; i < ARRAY_SIZE(valid_ranges); ++i)
{ /* just has to match one of these rows */
if (valid_ranges[i].len == charlen)
{
good = 1;
for (j = 0; j < charlen; ++j)
{
if (data[j] < valid_ranges[i].bytes[j].lower ||
data[j] > valid_ranges[i].bytes[j].upper)
{
good = 0; /* failed the check */
break;
}
}
if (good)
break;
}
}
if (good)
rc = data + charlen;
exit:
return rc;
}
/**
* Validate a length-delimited string has only UTF-8 characters
* @param len the length of the string in "data"
* @param data the bytes to check for valid UTF-8 characters
* @return 1 (true) if the string has only UTF-8 characters, 0 (false) otherwise
*/
int UTF8_validate(int len, const char* data)
{
const char* curdata = NULL;
int rc = 0;
FUNC_ENTRY;
if (len == 0 || data == NULL)
{
rc = 1;
goto exit;
}
curdata = UTF8_char_validate(len, data);
while (curdata && (curdata < data + len))
curdata = UTF8_char_validate((int)(data + len - curdata), curdata);
rc = curdata != NULL;
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Validate a null-terminated string has only UTF-8 characters
* @param string the string to check for valid UTF-8 characters
* @return 1 (true) if the string has only UTF-8 characters, 0 (false) otherwise
*/
int UTF8_validateString(const char* string)
{
int rc = 0;
FUNC_ENTRY;
if (string != NULL)
{
rc = UTF8_validate((int)strlen(string), string);
}
FUNC_EXIT_RC(rc);
return rc;
}
#if defined(UNIT_TESTS)
#include <stdio.h>
typedef struct
{
int len;
char data[20];
} tests;
tests valid_strings[] =
{
{3, "hjk" },
{7, {0x41, 0xE2, 0x89, 0xA2, 0xCE, 0x91, 0x2E} },
{3, {'f', 0xC9, 0xB1 } },
{9, {0xED, 0x95, 0x9C, 0xEA, 0xB5, 0xAD, 0xEC, 0x96, 0xB4} },
{9, {0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE8, 0xAA, 0x9E} },
{4, {0x2F, 0x2E, 0x2E, 0x2F} },
{7, {0xEF, 0xBB, 0xBF, 0xF0, 0xA3, 0x8E, 0xB4} },
};
tests invalid_strings[] =
{
{2, {0xC0, 0x80} },
{5, {0x2F, 0xC0, 0xAE, 0x2E, 0x2F} },
{6, {0xED, 0xA1, 0x8C, 0xED, 0xBE, 0xB4} },
{1, {0xF4} },
};
int main (int argc, char *argv[])
{
int i, failed = 0;
for (i = 0; i < ARRAY_SIZE(valid_strings); ++i)
{
if (!UTF8_validate(valid_strings[i].len, valid_strings[i].data))
{
printf("valid test %d failed\n", i);
failed = 1;
}
else
printf("valid test %d passed\n", i);
}
for (i = 0; i < ARRAY_SIZE(invalid_strings); ++i)
{
if (UTF8_validate(invalid_strings[i].len, invalid_strings[i].data))
{
printf("invalid test %d failed\n", i);
failed = 1;
}
else
printf("invalid test %d passed\n", i);
}
if (failed)
printf("Failed\n");
else
printf("Passed\n");
//Don't crash on null data
UTF8_validateString(NULL);
UTF8_validate(1, NULL);
UTF8_char_validate(1, NULL);
return 0;
} /* End of main function*/
#endif