Merge: Snapmaker Orca 2.1.2

This commit is contained in:
xiaoyeliu
2025-11-17 10:04:25 +08:00
parent 737948be1f
commit e89263e51a
1147 changed files with 668188 additions and 15290 deletions

173
src/mqtt/CMakeLists.txt Normal file
View File

@@ -0,0 +1,173 @@
# CMakeLists.txt
#
# Top-level CMake file for the Paho C++ library.
#
#*******************************************************************************
# This is part of the Paho MQTT C++ client library.
#
# Copyright (c) 2016-2017, Guilherme Maciel Ferreira
# Copyright (c) 2017-2023, Frank Pagliughi
#
# 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
# http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Guilherme Maciel Ferreira - initial version
# Frank Pagliughi
#*******************************************************************************/
cmake_minimum_required(VERSION 3.5)
project(PahoMqttCpp VERSION "1.4.0")
## --- Build options ---
if(WIN32)
option(PAHO_BUILD_STATIC "Build static library" TRUE)
option(PAHO_BUILD_SHARED "Build shared library (DLL)" FALSE)
option(PAHO_WITH_SSL "Build SSL-enabled library" ON)
else()
option(PAHO_BUILD_STATIC "Build static library" TRUE)
option(PAHO_BUILD_SHARED "Build shared library" FALSE)
option(PAHO_WITH_SSL "Build SSL-enabled library" ON)
option(PAHO_BUILD_DEB_PACKAGE "Build debian package" FALSE)
endif()
option(PAHO_BUILD_SAMPLES "Build sample/example programs" FALSE)
option(PAHO_BUILD_EXAMPLES "Build sample/example programs" FALSE)
option(PAHO_BUILD_TESTS "Build tests (requires Catch2)" FALSE)
option(PAHO_BUILD_DOCUMENTATION "Create and install the API documentation (requires Doxygen)" FALSE)
option(PAHO_WITH_MQTT_C "Build Paho C from the internal GIT submodule." TRUE)
if(NOT PAHO_BUILD_SHARED AND NOT PAHO_BUILD_STATIC)
message(FATAL_ERROR "You must set either PAHO_BUILD_SHARED, PAHO_BUILD_STATIC, or both")
endif()
## --- Find Paho C or build it, if reqested ---
if(PAHO_WITH_SSL)
find_package(OpenSSL REQUIRED)
set(PAHO_MQTT_C_LIB paho-mqtt3as)
else()
set(PAHO_MQTT_C_LIB paho-mqtt3a)
endif()
if(PAHO_WITH_MQTT_C)
message(STATUS "Paho C: Bundled")
## Build the Paho C library from the submodule
set(PAHO_ENABLE_TESTING FALSE CACHE BOOL "No Paho C tests")
set(PAHO_HIGH_PERFORMANCE TRUE CACHE BOOL "Paho C high performance")
# TODO: Remove this when the next Paho C update is released.
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
add_subdirectory(${PROJECT_SOURCE_DIR}/externals/paho-mqtt-c)
## Alias namespace so that the full names can be used with the subdir.
if(PAHO_BUILD_SHARED)
add_library(eclipse-paho-mqtt-c::paho-mqtt3a ALIAS paho-mqtt3a)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3a)
if(PAHO_WITH_SSL)
add_library(eclipse-paho-mqtt-c::paho-mqtt3as ALIAS paho-mqtt3as)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3as)
endif()
endif()
if(PAHO_BUILD_STATIC)
add_library(eclipse-paho-mqtt-c::paho-mqtt3a-static ALIAS paho-mqtt3a-static)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3a-static)
if(PAHO_WITH_SSL)
add_library(eclipse-paho-mqtt-c::paho-mqtt3as-static ALIAS paho-mqtt3as-static)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3as-static)
endif()
endif()
## install paho.mqtt.c library (appending to PahoMqttCpp export)
install(TARGETS ${PAHO_MQTT_C_LIBS}
EXPORT PahoMqttCpp
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
else()
find_package(eclipse-paho-mqtt-c REQUIRED)
endif()
## --- C++11 build flags ---
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Generate position-independent code (-fPIC on UNIX)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# --- System Details ---
include(GNUInstallDirs)
if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(LIBS_SYSTEM ws2_32)
endif()
## --- Build directories ---
# --- The headers ---
add_subdirectory(include/mqtt)
# For the paho_mqtt_c module
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
add_subdirectory(src)
# --- Documentation ---
if(PAHO_BUILD_DOCUMENTATION)
add_subdirectory(doc)
endif()
# --- Default library for samples and unit tests ---
if(PAHO_BUILD_SHARED)
set(PAHO_CPP_LIB paho-mqttpp3)
else()
set(PAHO_CPP_LIB paho-mqttpp3-static)
endif()
# --- Example Apps ---
if(PAHO_BUILD_SAMPLES OR PAHO_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
# --- Unit Tests ---
if(PAHO_BUILD_TESTS)
add_subdirectory(test/unit)
endif()
## --- Packaging settings ---
if(WIN32)
set(CPACK_GENERATOR "ZIP")
elseif(UNIX)
if(PAHO_BUILD_DEB_PACKAGE)
set(CPACK_GENERATOR "DEB")
include(cmake/CPackDebConfig.cmake)
else()
set(CPACK_GENERATOR "TGZ")
endif()
endif()
include(CPack)
add_subdirectory(cmake)

View File

@@ -0,0 +1,46 @@
# CMakeLists.txt
#
# CMake export file for the Paho C++ library.
#
#*******************************************************************************
# This is part of the Paho MQTT C++ client library.
#
# Copyright (c) 2017-2023, Frank Pagliughi
#
# 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
# http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#*******************************************************************************/
set(package_name PahoMqttCpp)
configure_file(${package_name}Config.cmake.in ${package_name}Config.cmake @ONLY)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
export(EXPORT ${package_name}
FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Targets.cmake"
NAMESPACE ${package_name}::
)
install(EXPORT ${package_name}
DESTINATION lib/cmake/${package_name}
FILE ${package_name}Targets.cmake
NAMESPACE ${package_name}::
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
DESTINATION lib/cmake/${package_name}
)

View File

@@ -0,0 +1,10 @@
if(CPACK_GENERATOR MATCHES "DEB")
set(CPACK_PACKAGE_NAME "libpaho-mqtt.cpp")
set(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME})
set(CPACK_PACKAGE_CONTACT "Eclipse")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Eclipse Paho MQTT C++ client")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER " <>")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_VERSION ${PACKAGE_VERSION})
set(CPACK_DEBIAN_PACKAGE_SECTION "net")
endif()

View File

@@ -0,0 +1,19 @@
# save build-time options
set(PAHO_BUILD_STATIC @PAHO_BUILD_STATIC@)
set(PAHO_BUILD_SHARED @PAHO_BUILD_SHARED@)
set(PAHO_WITH_SSL @PAHO_WITH_SSL@)
set(PAHO_WITH_MQTT_C @PAHO_WITH_MQTT_C@)
include(CMakeFindDependencyMacro)
find_dependency(Threads REQUIRED)
if (NOT PAHO_WITH_MQTT_C)
find_dependency(eclipse-paho-mqtt-c REQUIRED)
endif()
if (PAHO_WITH_SSL)
find_dependency(OpenSSL REQUIRED)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@package_name@Targets.cmake")

View File

@@ -0,0 +1,131 @@
#*******************************************************************************
# Copyright (c) 2015, 2023 logi.cals GmbH 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:
# Rainer Poisel - initial version
# Genis Riera Perez - Add support for building debian package
#*******************************************************************************/
# Note: on OS X you should install XCode and the associated command-line tools
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
PROJECT("Eclipse Paho C" C)
MESSAGE(STATUS "CMake version: " ${CMAKE_VERSION})
MESSAGE(STATUS "CMake system name: " ${CMAKE_SYSTEM_NAME})
SET(CMAKE_SCRIPTS "${CMAKE_SOURCE_DIR}/cmake")
SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
## build settings
file(READ version.major PAHO_VERSION_MAJOR)
file(READ version.minor PAHO_VERSION_MINOR)
file(READ version.patch PAHO_VERSION_PATCH)
SET(CLIENT_VERSION ${PAHO_VERSION_MAJOR}.${PAHO_VERSION_MINOR}.${PAHO_VERSION_PATCH})
INCLUDE(GNUInstallDirs)
STRING(TIMESTAMP BUILD_TIMESTAMP UTC)
MESSAGE(STATUS "Timestamp is ${BUILD_TIMESTAMP}")
IF(WIN32)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE -DWIN32_LEAN_AND_MEAN)
ELSEIF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
ADD_DEFINITIONS(-DOSX)
ENDIF()
## build options
SET(PAHO_WITH_SSL ON CACHE BOOL "Flag that defines whether to build ssl-enabled binaries too. " FORCE)
SET(PAHO_WITH_LIBUUID FALSE CACHE BOOL "Flag that defines whether libuuid or a custom uuid implementation should be used")
SET(PAHO_BUILD_SHARED TRUE CACHE BOOL "Build shared library")
SET(PAHO_BUILD_STATIC FALSE CACHE BOOL "Build static library")
SET(PAHO_BUILD_DOCUMENTATION FALSE CACHE BOOL "Create and install the HTML based API documentation (requires Doxygen)")
SET(PAHO_BUILD_SAMPLES FALSE CACHE BOOL "Build sample programs")
SET(PAHO_BUILD_DEB_PACKAGE FALSE CACHE BOOL "Build debian package")
SET(PAHO_ENABLE_TESTING TRUE CACHE BOOL "Build tests and run")
SET(PAHO_ENABLE_CPACK TRUE CACHE BOOL "Enable CPack")
SET(PAHO_HIGH_PERFORMANCE FALSE CACHE BOOL "Disable tracing and heap tracking")
SET(PAHO_USE_SELECT FALSE CACHE BOOL "Revert to select system call instead of poll")
IF (PAHO_HIGH_PERFORMANCE)
ADD_DEFINITIONS(-DHIGH_PERFORMANCE=1)
ENDIF()
IF (PAHO_USE_SELECT)
ADD_DEFINITIONS(-DUSE_SELECT=1)
ENDIF()
IF (PAHO_WITH_LIBUUID)
ADD_DEFINITIONS(-DUSE_LIBUUID=1)
ENDIF()
IF (NOT PAHO_BUILD_SHARED AND NOT PAHO_BUILD_STATIC)
MESSAGE(FATAL_ERROR "You must set either PAHO_BUILD_SHARED, PAHO_BUILD_STATIC, or both")
ENDIF()
IF (PAHO_BUILD_SAMPLES AND NOT PAHO_WITH_SSL)
MESSAGE(FATAL_ERROR "You must build with SSL to build the samples")
ENDIF()
IF(PAHO_BUILD_DEB_PACKAGE)
set(CMAKE_INSTALL_DOCDIR share/doc/libpaho-mqtt)
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY ">=")
ENDIF()
ADD_SUBDIRECTORY(src)
IF(PAHO_BUILD_SAMPLES)
ADD_SUBDIRECTORY(src/samples)
ENDIF()
IF(PAHO_BUILD_DOCUMENTATION)
ADD_SUBDIRECTORY(doc)
ENDIF()
IF (PAHO_ENABLE_CPACK)
### packaging settings
FILE(GLOB samples "src/samples/*.c")
INSTALL(FILES ${samples} DESTINATION ${CMAKE_INSTALL_DOCDIR}/samples)
SET(CPACK_PACKAGE_VENDOR "Eclipse Paho")
SET(CPACK_PACKAGE_NAME "Eclipse-Paho-MQTT-C")
IF (WIN32)
SET(CPACK_GENERATOR "ZIP")
ELSEIF(PAHO_BUILD_DEB_PACKAGE)
SET(CPACK_GENERATOR "DEB")
CONFIGURE_FILE(${CMAKE_SCRIPTS}/CPackDebConfig.cmake.in
${CMAKE_BINARY_DIR}/CPackDebConfig.cmake @ONLY)
SET(CPACK_PROJECT_CONFIG_FILE ${CMAKE_BINARY_DIR}/CPackDebConfig.cmake)
ELSE()
SET(CPACK_GENERATOR "TGZ")
ENDIF()
ELSE()
FILE(GLOB samples "src/samples/*.c")
INSTALL(FILES ${samples} DESTINATION ${CMAKE_INSTALL_DOCDIR})
ENDIF()
SET(CPACK_PACKAGE_VERSION_MAJOR ${PAHO_VERSION_MAJOR})
SET(CPACK_PACKAGE_VERSION_MINOR ${PAHO_VERSION_MINOR})
SET(CPACK_PACKAGE_VERSION_PATCH ${PAHO_VERSION_PATCH})
INCLUDE(CPack)
IF(PAHO_ENABLE_TESTING)
ENABLE_TESTING()
INCLUDE_DIRECTORIES(test src)
ADD_SUBDIRECTORY(test)
ELSE()
INCLUDE_DIRECTORIES(src)
ENDIF()
# 添加调试输出
MESSAGE(STATUS "Root CMake PAHO_WITH_SSL is set to: ${PAHO_WITH_SSL}")

View File

@@ -0,0 +1,88 @@
IF (CPACK_GENERATOR MATCHES "DEB")
FIND_PROGRAM(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
IF (DPKG_PROGRAM)
EXECUTE_PROCESS(
COMMAND ${DPKG_PROGRAM} --print-architecture
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
ELSE (DPKG_PROGRAM)
MESSAGE(FATAL_ERROR "Could not find an architecture for the package")
ENDIF (DPKG_PROGRAM)
EXECUTE_PROCESS(
COMMAND lsb_release -si
OUTPUT_VARIABLE CPACK_DEBIAN_DIST_NAME
RESULT_VARIABLE DIST_NAME_STATUS
OUTPUT_STRIP_TRAILING_WHITESPACE
)
IF (DIST_NAME_STATUS)
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution name")
ENDIF (DIST_NAME_STATUS)
IF (CPACK_DEBIAN_DIST_NAME STREQUAL "")
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution name")
ENDIF ()
EXECUTE_PROCESS(
COMMAND lsb_release -sc
OUTPUT_VARIABLE CPACK_DEBIAN_DIST_CODE
RESULT_VARIABLE DIST_CODE_STATUS
OUTPUT_STRIP_TRAILING_WHITESPACE
)
IF (DIST_NAME_STATUS)
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution codename")
ENDIF (DIST_NAME_STATUS)
IF (CPACK_DEBIAN_DIST_CODE STREQUAL "")
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution codename")
ENDIF ()
SET(CPACK_PACKAGE_VERSION_MAJOR @PAHO_VERSION_MAJOR@)
SET(CPACK_PACKAGE_VERSION_MINOR @PAHO_VERSION_MINOR@)
SET(CPACK_PACKAGE_VERSION_PATCH @PAHO_VERSION_PATCH@)
SET(PACKAGE_VERSION
"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
IF (PACKAGE_VERSION STREQUAL "")
MESSAGE(FATAL_ERROR "Could not find a version number for the package")
ENDIF ()
SET(PAHO_WITH_SSL @PAHO_WITH_SSL@)
MESSAGE("Package version: ${PACKAGE_VERSION}")
MESSAGE("Package built for: ${CPACK_DEBIAN_DIST_NAME} ${CPACK_DEBIAN_DIST_CODE}")
IF(PAHO_WITH_SSL)
MESSAGE("Package built with ssl-enabled binaries too")
ENDIF()
# Additional lines to a paragraph should start with " "; paragraphs should
# be separated with a " ." line
SET(CPACK_PACKAGE_NAME "libpaho-mqtt")
SET(CPACK_PACKAGE_CONTACT "Eclipse")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Eclipse Paho MQTT C client")
SET(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME})
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER
"Genis Riera Perez <genis.riera.perez@gmail.com>")
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Eclipse Paho MQTT C client library")
SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
SET(CPACK_DEBIAN_PACKAGE_VERSION ${PACKAGE_VERSION})
SET(CPACK_DEBIAN_PACKAGE_SECTION "net")
SET(CPACK_DEBIAN_PACKAGE_CONFLICTS ${CPACK_PACKAGE_NAME})
SET(CPACK_PACKAGE_FILE_NAME
"${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
UNSET(PACKAGE_VERSION CACHE)
UNSET(CPACK_DEBIAN_PACKAGE_VERSION CACHE)
#
# From CMakeDebHelper
# See http://www.cmake.org/Wiki/CMake:CPackPackageGenerators#Overall_usage_.28common_to_all_generators.29
#
# When the DEB-generator runs, we want him to run our install-script
#set( CPACK_INSTALL_SCRIPT ${CPACK_DEBIAN_INSTALL_SCRIPT} )
ENDIF()

View File

@@ -0,0 +1,10 @@
# path to compiler and utilities
# specify the cross compiler
SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
# Name of the target platform
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_PROCESSOR arm)
# Version of the system
SET(CMAKE_SYSTEM_VERSION 1)

View File

@@ -0,0 +1,15 @@
# Name of the target platform
SET(CMAKE_SYSTEM_NAME Windows)
# Version of the system
SET(CMAKE_SYSTEM_VERSION 1)
# specify the cross compiler
SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
SET(CMAKE_RC_COMPILER_ENV_VAR "RC")
SET(CMAKE_RC_COMPILER "")
SET(CMAKE_SHARED_LINKER_FLAGS
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
SET(CMAKE_EXE_LINKER_FLAGS
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)

View File

@@ -0,0 +1,15 @@
# Name of the target platform
SET(CMAKE_SYSTEM_NAME Windows)
# Version of the system
SET(CMAKE_SYSTEM_VERSION 1)
# specify the cross compiler
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
SET(CMAKE_RC_COMPILER_ENV_VAR "RC")
SET(CMAKE_RC_COMPILER "")
SET(CMAKE_SHARED_LINKER_FLAGS
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
SET(CMAKE_EXE_LINKER_FLAGS
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)

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) */

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 */

View File

@@ -0,0 +1,380 @@
#*******************************************************************************
# Copyright (c) 2015, 2023 logi.cals GmbH 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:
# Rainer Poisel - initial version
# Ian Craggs (IBM Corp.) - merge master
# Ian Craggs - update for MQTTV5 support
#*******************************************************************************/
# Note: on OS X you should install XCode and the associated command-line tools
## compilation/linkage settings
CONFIGURE_FILE(VersionInfo.h.in
${CMAKE_BINARY_DIR}/VersionInfo.h
@ONLY
)
SET(common_src
MQTTTime.c
MQTTProtocolClient.c
Clients.c
utf-8.c
MQTTPacket.c
MQTTPacketOut.c
Messages.c
Tree.c
Socket.c
Log.c
MQTTPersistence.c
Thread.c
MQTTProtocolOut.c
MQTTPersistenceDefault.c
SocketBuffer.c
LinkedList.c
MQTTProperties.c
MQTTReasonCodes.c
Base64.c
SHA1.c
WebSocket.c
Proxy.c
)
IF (NOT PAHO_HIGH_PERFORMANCE)
SET(common_src ${common_src}
StackTrace.c
Heap.c
)
ENDIF()
IF (WIN32)
SET(LIBS_SYSTEM ws2_32 crypt32 RpcRT4)
ELSEIF (UNIX)
IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
SET(LIBS_SYSTEM c dl pthread rt)
# anl is only available with glibc so check if it is found before using
# it or build will fail on uclibc or musl
FIND_LIBRARY(LIB_ANL anl)
IF(LIB_ANL)
SET(LIBS_SYSTEM "${LIBS_SYSTEM}" anl)
ENDIF()
IF(PAHO_WITH_LIBUUID)
SET(LIBS_SYSTEM "${LIBS_SYSTEM}" uuid)
ENDIF()
ADD_DEFINITIONS(-D_GNU_SOURCE -fvisibility=hidden)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Android")
SET(LIBS_SYSTEM c dl)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
SET(LIBS_SYSTEM compat pthread)
ELSE()
SET(LIBS_SYSTEM c pthread)
ENDIF()
ENDIF()
IF (PAHO_BUILD_SHARED)
# common compilation for libpaho-mqtt3c and libpaho-mqtt3a
ADD_LIBRARY(common_obj OBJECT ${common_src})
SET_TARGET_PROPERTIES(common_obj PROPERTIES
POSITION_INDEPENDENT_CODE ON
COMPILE_DEFINITIONS "PAHO_MQTT_EXPORTS=1")
ADD_EXECUTABLE(MQTTVersion MQTTVersion.c)
SET_TARGET_PROPERTIES(MQTTVersion PROPERTIES
POSITION_INDEPENDENT_CODE ON
COMPILE_DEFINITIONS "PAHO_MQTT_IMPORTS=1")
ENDIF()
IF (PAHO_BUILD_STATIC)
ADD_LIBRARY(common_obj_static OBJECT ${common_src})
SET_TARGET_PROPERTIES(common_obj_static PROPERTIES
POSITION_INDEPENDENT_CODE ON
COMPILE_DEFINITIONS "PAHO_MQTT_STATIC=1")
ENDIF()
IF (PAHO_BUILD_SHARED)
ADD_LIBRARY(paho-mqtt3c SHARED $<TARGET_OBJECTS:common_obj> MQTTClient.c)
ADD_LIBRARY(paho-mqtt3a SHARED $<TARGET_OBJECTS:common_obj> MQTTAsync.c MQTTAsyncUtils.c)
TARGET_LINK_LIBRARIES(paho-mqtt3c ${LIBS_SYSTEM})
TARGET_LINK_LIBRARIES(paho-mqtt3a ${LIBS_SYSTEM})
TARGET_LINK_LIBRARIES(MQTTVersion paho-mqtt3a paho-mqtt3c ${LIBS_SYSTEM})
SET_TARGET_PROPERTIES(
paho-mqtt3c paho-mqtt3a PROPERTIES
VERSION ${CLIENT_VERSION}
SOVERSION ${PAHO_VERSION_MAJOR}
COMPILE_DEFINITIONS "PAHO_MQTT_EXPORTS=1")
IF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
SET(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
SET(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
ELSEIF (NOT WIN32)
SET(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
SET(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
ENDIF()
IF (NOT WIN32)
SET_TARGET_PROPERTIES(
paho-mqtt3c PROPERTIES
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}")
SET_TARGET_PROPERTIES(
paho-mqtt3a PROPERTIES
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}")
ENDIF()
FOREACH(TARGET paho-mqtt3c paho-mqtt3a)
TARGET_INCLUDE_DIRECTORIES(${TARGET}
PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PRIVATE
${CMAKE_BINARY_DIR})
ENDFOREACH()
INSTALL(TARGETS paho-mqtt3c paho-mqtt3a
EXPORT eclipse-paho-mqtt-cTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
INSTALL(TARGETS MQTTVersion
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
ENDIF()
IF (PAHO_BUILD_STATIC)
ADD_LIBRARY(paho-mqtt3c-static STATIC $<TARGET_OBJECTS:common_obj_static> MQTTClient.c)
ADD_LIBRARY(paho-mqtt3a-static STATIC $<TARGET_OBJECTS:common_obj_static> MQTTAsync.c MQTTAsyncUtils.c)
TARGET_LINK_LIBRARIES(paho-mqtt3c-static ${LIBS_SYSTEM})
TARGET_LINK_LIBRARIES(paho-mqtt3a-static ${LIBS_SYSTEM})
IF (NOT WIN32)
SET_TARGET_PROPERTIES(paho-mqtt3c-static PROPERTIES OUTPUT_NAME paho-mqtt3c)
SET_TARGET_PROPERTIES(paho-mqtt3a-static PROPERTIES OUTPUT_NAME paho-mqtt3a)
ENDIF()
SET_TARGET_PROPERTIES(
paho-mqtt3c-static paho-mqtt3a-static PROPERTIES
VERSION ${CLIENT_VERSION}
SOVERSION ${PAHO_VERSION_MAJOR}
COMPILE_DEFINITIONS "PAHO_MQTT_STATIC=1")
FOREACH(TARGET paho-mqtt3c-static paho-mqtt3a-static)
TARGET_INCLUDE_DIRECTORIES(${TARGET}
PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PRIVATE
${CMAKE_BINARY_DIR})
ENDFOREACH()
message("PAHO_WITH_SSL: ${PAHO_WITH_SSL}")
if(PAHO_WITH_SSL)
target_compile_definitions(paho-mqtt3a-static PRIVATE OPENSSL=1)
message("paho-mqtt3a-static is compiled with SSL")
endif()
IF (NOT PAHO_BUILD_SHARED)
INSTALL(TARGETS paho-mqtt3c-static paho-mqtt3a-static
EXPORT eclipse-paho-mqtt-cTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
ELSE()
INSTALL(TARGETS paho-mqtt3c-static paho-mqtt3a-static
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
ENDIF()
ENDIF()
INSTALL(FILES MQTTAsync.h MQTTClient.h MQTTClientPersistence.h MQTTProperties.h MQTTReasonCodes.h MQTTSubscribeOpts.h MQTTExportDeclarations.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
IF (PAHO_WITH_SSL)
find_package(OpenSSL REQUIRED)
if(NOT OPENSSL_FOUND)
message(FATAL_ERROR "OpenSSL not found")
endif()
message(STATUS "OpenSSL found: ${OPENSSL_INCLUDE_DIR}")
IF (PAHO_BUILD_SHARED)
## common compilation for libpaho-mqtt3cs and libpaho-mqtt3as
## Note: SSL libraries must be recompiled due ifdefs
ADD_LIBRARY(common_ssl_obj OBJECT ${common_src})
TARGET_INCLUDE_DIRECTORIES(common_ssl_obj PUBLIC ${OPENSSL_INCLUDE_DIR})
SET_PROPERTY(TARGET common_ssl_obj PROPERTY POSITION_INDEPENDENT_CODE ON)
SET_PROPERTY(TARGET common_ssl_obj PROPERTY COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_EXPORTS=1")
ADD_LIBRARY(paho-mqtt3cs SHARED $<TARGET_OBJECTS:common_ssl_obj> MQTTClient.c SSLSocket.c)
ADD_LIBRARY(paho-mqtt3as SHARED $<TARGET_OBJECTS:common_ssl_obj> MQTTAsync.c MQTTAsyncUtils.c SSLSocket.c)
SET_TARGET_PROPERTIES(
paho-mqtt3cs paho-mqtt3as PROPERTIES
VERSION ${CLIENT_VERSION}
SOVERSION ${PAHO_VERSION_MAJOR}
COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_EXPORTS=1")
IF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
SET(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
SET(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
ELSEIF (NOT WIN32)
SET(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
SET(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
ENDIF()
IF (NOT WIN32)
SET_TARGET_PROPERTIES(
paho-mqtt3cs PROPERTIES
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}")
SET_TARGET_PROPERTIES(
paho-mqtt3as PROPERTIES
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}")
ENDIF()
FOREACH(TARGET paho-mqtt3cs paho-mqtt3as)
TARGET_INCLUDE_DIRECTORIES(${TARGET}
PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PRIVATE
${CMAKE_BINARY_DIR})
TARGET_LINK_LIBRARIES(${TARGET}
PUBLIC
OpenSSL::SSL OpenSSL::Crypto ${LIBS_SYSTEM})
ENDFOREACH()
INSTALL(TARGETS paho-mqtt3cs paho-mqtt3as
EXPORT eclipse-paho-mqtt-cTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
ENDIF()
IF (PAHO_BUILD_STATIC)
## common compilation for libpaho-mqtt3cs and libpaho-mqtt3as
## Note: SSL libraries must be recompiled due ifdefs
ADD_LIBRARY(common_ssl_obj_static OBJECT ${common_src})
TARGET_INCLUDE_DIRECTORIES(common_ssl_obj_static PUBLIC ${OPENSSL_INCLUDE_DIR})
SET_PROPERTY(TARGET common_ssl_obj_static PROPERTY POSITION_INDEPENDENT_CODE ON)
SET_PROPERTY(TARGET common_ssl_obj_static PROPERTY COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_STATIC=1")
ADD_LIBRARY(paho-mqtt3cs-static STATIC $<TARGET_OBJECTS:common_ssl_obj_static> MQTTClient.c SSLSocket.c)
ADD_LIBRARY(paho-mqtt3as-static STATIC $<TARGET_OBJECTS:common_ssl_obj_static> MQTTAsync.c MQTTAsyncUtils.c SSLSocket.c)
SET_TARGET_PROPERTIES(
paho-mqtt3cs-static paho-mqtt3as-static PROPERTIES
VERSION ${CLIENT_VERSION}
SOVERSION ${PAHO_VERSION_MAJOR}
COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_STATIC=1")
IF (NOT WIN32)
SET_TARGET_PROPERTIES(paho-mqtt3cs-static PROPERTIES OUTPUT_NAME paho-mqtt3cs)
SET_TARGET_PROPERTIES(paho-mqtt3as-static PROPERTIES OUTPUT_NAME paho-mqtt3as)
ENDIF()
IF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
SET(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
SET(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
ELSEIF (NOT WIN32)
SET(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
SET(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
ENDIF()
IF (NOT WIN32)
SET_TARGET_PROPERTIES(
paho-mqtt3cs-static PROPERTIES
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}")
SET_TARGET_PROPERTIES(
paho-mqtt3as-static PROPERTIES
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}")
ENDIF()
IF (NOT PAHO_BUILD_SHARED)
INSTALL(TARGETS paho-mqtt3cs-static paho-mqtt3as-static
EXPORT eclipse-paho-mqtt-cTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
ELSE()
INSTALL(TARGETS paho-mqtt3cs-static paho-mqtt3as-static
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
ENDIF()
FOREACH(TARGET paho-mqtt3cs-static paho-mqtt3as-static)
TARGET_INCLUDE_DIRECTORIES(${TARGET}
PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PRIVATE
${CMAKE_BINARY_DIR})
TARGET_LINK_LIBRARIES(${TARGET}
PUBLIC
OpenSSL::SSL OpenSSL::Crypto ${LIBS_SYSTEM})
ENDFOREACH()
# 为所有静态库目标添加 OpenSSL 包含目录
IF (PAHO_BUILD_STATIC)
# 已有的 paho-mqtt3a-static
target_include_directories(paho-mqtt3a-static
PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
# 为 paho-mqtt3cs-static 添加
target_include_directories(paho-mqtt3cs-static
PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
# 为 paho-mqtt3c-static 添加
target_include_directories(paho-mqtt3c-static
PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
# 确保所有静态库都定义了 OPENSSL 宏
foreach(target paho-mqtt3a-static paho-mqtt3cs-static paho-mqtt3c-static)
target_compile_definitions(${target} PRIVATE OPENSSL=1)
message("${target} is compiled with SSL")
endforeach()
ENDIF()
ENDIF()
ENDIF()
INSTALL(EXPORT eclipse-paho-mqtt-cTargets
FILE eclipse-paho-mqtt-cConfig.cmake
NAMESPACE eclipse-paho-mqtt-c::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c)
INCLUDE(CMakePackageConfigHelpers)
WRITE_BASIC_PACKAGE_VERSION_FILE("eclipse-paho-mqtt-cConfigVersion.cmake"
VERSION ${CLIENT_VERSION}
COMPATIBILITY SameMajorVersion)
INSTALL(FILES
"${CMAKE_CURRENT_BINARY_DIR}/eclipse-paho-mqtt-cConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c)
# Base64 test
ADD_EXECUTABLE( Base64Test EXCLUDE_FROM_ALL Base64.c Base64.h )
TARGET_COMPILE_DEFINITIONS( Base64Test PUBLIC "-DBASE64_TEST" )
IF (PAHO_WITH_SSL)
ADD_EXECUTABLE( Base64TestOpenSSL EXCLUDE_FROM_ALL Base64.c Base64.h )
TARGET_LINK_LIBRARIES( Base64TestOpenSSL OpenSSL::SSL OpenSSL::Crypto)
TARGET_COMPILE_DEFINITIONS( Base64TestOpenSSL PUBLIC "-DBASE64_TEST -DOPENSSL=1" )
ENDIF (PAHO_WITH_SSL)
# SHA1 test
ADD_EXECUTABLE( Sha1Test EXCLUDE_FROM_ALL SHA1.c SHA1.h )
TARGET_COMPILE_DEFINITIONS( Sha1Test PUBLIC "-DSHA1_TEST" )
IF (PAHO_WITH_SSL)
ADD_EXECUTABLE( Sha1TestOpenSSL EXCLUDE_FROM_ALL SHA1.c SHA1.h )
TARGET_LINK_LIBRARIES( Sha1TestOpenSSL OpenSSL::SSL OpenSSL::Crypto)
TARGET_COMPILE_DEFINITIONS( Sha1TestOpenSSL PUBLIC "-DSHA1_TEST -DOPENSSL=1" )
ENDIF (PAHO_WITH_SSL)
if(WIN32)
option(PAHO_BUILD_STATIC "Build static library" TRUE)
option(PAHO_BUILD_SHARED "Build shared library (DLL)" FALSE)
option(PAHO_WITH_SSL "Build SSL-enabled library" FALSE)
else()
option(PAHO_BUILD_STATIC "Build static library" TRUE)
option(PAHO_BUILD_SHARED "Build shared library" FALSE)
option(PAHO_WITH_SSL "Build SSL-enabled library" TRUE)
option(PAHO_BUILD_DEB_PACKAGE "Build debian package" FALSE)
endif()

View File

@@ -0,0 +1,55 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 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 == *(SOCKET*)b;
}

View File

@@ -0,0 +1,174 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 IBM Corp. and Ian Craggs
*
* 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 <stdint.h>
#include "MQTTTime.h"
#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
#endif
#if defined(OPENSSL)
#include <openssl/ssl.h>
#endif
#include "MQTTClient.h"
#include "LinkedList.h"
#include "MQTTClientPersistence.h"
#include "Socket.h"
/**
* Stored publication data to minimize copying
*/
typedef struct
{
char *topic;
int topiclen;
char* payload;
int payloadlen;
int refcount;
uint8_t mask[4];
} 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
{
SOCKET 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;
unsigned int ping_due : 1; /**< we couldn't send a ping so we should send one when we can */
signed int connect_state : 4;
START_TIME_TYPE ping_due_time; /**< the time at which the ping should have been sent (ping_due) */
networkHandles net; /**< network info for this client */
int msgID; /**< the MQTT message id */
int keepAliveInterval; /**< the MQTT keep alive interval */
int retryInterval; /**< the MQTT retry interval for QoS > 0 */
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 */
int connect_count; /**< the number of outbound messages on reconnect - to ensure we send them all */
int connect_sent; /**< the current number of outbound messages on reconnect that we've sent */
List* messageQueue; /**< inbound complete but undelivered messages */
List* outboundQueue; /**< outbound queued messages */
unsigned int qentry_seqno;
void* phandle; /**< the persistence handle */
MQTTClient_persistence* persistence; /**< a persistence implementation */
MQTTPersistence_beforeWrite* beforeWrite; /**< persistence write callback */
MQTTPersistence_afterRead* afterRead; /**< persistence read callback */
void* beforeWrite_context; /**< context to be used with the persistence beforeWrite callbacks */
void* afterRead_context; /**< context to be used with the persistence afterRead callback */
void* context; /**< calling context - used when calling disconnect_internal */
int MQTTVersion; /**< the version of MQTT being used, 3, 4 or 5 */
unsigned int sessionExpiry; /**< MQTT 5 session expiry */
char* httpProxy; /**< HTTP proxy */
char* httpsProxy; /**< HTTPS proxy */
#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

View File

@@ -0,0 +1,530 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 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 <inttypes.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 uint64_t eyecatcherType;
static eyecatcherType eyecatcher = (eyecatcherType)0x8888888888888888;
#define PRIeyecatcher PRIx64 /**< print eyecatcher in HEX notation */
/*#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;
Paho_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(Paho_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:
Paho_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 %" PRIeyecatcher " 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) */
{
Paho_thread_lock_mutex(heap_mutex);
if (Internal_heap_unlink(file, line, p))
free(((eyecatcherType*)p)-1);
Paho_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)
{
Paho_thread_lock_mutex(heap_mutex);
Internal_heap_unlink(file, line, p);
Paho_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;
Paho_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:
Paho_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;
Paho_thread_lock_mutex(heap_mutex);
e = TreeFind(&heap, ((eyecatcherType*)p)-1);
Paho_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;
Paho_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");
Paho_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: */

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 pointer to 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

View File

@@ -0,0 +1,22 @@
#if WINVER <= _WIN32_WINNT_WIN8
#define HTON(x) hton((uint64_t) (x), sizeof(x))
uint64_t hton(uint64_t x, size_t n)
{
uint64_t y = 0;
size_t i = 0;
for (i=0; i < n; ++i)
{
y = (y << 8) | (x & 0xff);
x = (x >> 8);
}
return y;
}
#define htons(x) (uint16_t) HTON(x)
#define htonl(x) (uint32_t) HTON(x)
#define htonll(x) (uint64_t) HTON(x)
#define ntohs(x) htons(x)
#define ntohl(x) htonl(x)
#define ntohll(x) htonll(x)
#endif

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

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

585
src/mqtt/externals/paho-mqtt-c/src/Log.c vendored Normal file
View File

@@ -0,0 +1,585 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 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 =
{
#if defined(HIGH_PERFORMANCE)
LOG_ERROR,
#else
TRACE_MINIMUM,
#endif
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;
thread_id_type 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 now_ts, last_ts;
#else
struct timeb now_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
{
size_t namelen = 0;
if ((trace_destination_name = malloc(strlen(envval) + 1)) == NULL)
{
free(trace_queue);
goto exit;
}
strcpy(trace_destination_name, envval);
namelen = strlen(envval) + 3;
if ((trace_destination_backup_name = malloc(namelen)) == NULL)
{
free(trace_queue);
free(trace_destination_name);
goto exit;
}
if (snprintf(trace_destination_backup_name, namelen, "%s.0", trace_destination_name) >= namelen)
trace_destination_backup_name[namelen-1] = '\0';
}
}
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 <= LOG_ERROR) /* the lowest we can go is LOG_ERROR */
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(&now_ts, NULL);
if (now_ts.tv_sec != last_ts.tv_sec || now_ts.tv_usec != last_ts.tv_usec)
#else
ftime(&now_ts);
if (now_ts.time != last_ts.time || now_ts.millitm != last_ts.millitm)
#endif
{
sametime_count = 0;
last_ts = now_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)
snprintf(&msg_buf[22], sizeof(msg_buf)-22, ".%.3lu ", cur_entry->ts.tv_usec / 1000L);
#else
snprintf(&msg_buf[22], sizeof(msg_buf)-22, ".%.3hu ", cur_entry->ts.millitm);
#endif
buf_pos = 27;
snprintf(msg_buf, sizeof(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), &now_ts, sizeof(now_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 */
Paho_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);
Paho_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, thread_id_type 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;
Paho_thread_lock_mutex(log_mutex);
cur_entry = Log_pretrace();
memcpy(&(cur_entry->ts), &now_ts, sizeof(now_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);
Paho_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

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 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
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#define thread_id_type DWORD
#else
#include <pthread.h>
#define thread_id_type pthread_t
#endif
/*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, thread_id_type, 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 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 implementation and documentation
*******************************************************************************/
#if !defined(MQTTASYNCUTILS_H_)
#define MQTTASYNCUTILS_H_
#include "MQTTPacket.h"
#include "Thread.h"
#define URI_TCP "tcp://"
#define URI_MQTT "mqtt://"
#define URI_WS "ws://"
#define URI_WSS "wss://"
enum MQTTAsync_threadStates
{
STOPPED, STARTING, RUNNING, STOPPING
};
typedef struct
{
MQTTAsync_message* msg;
char* topicName;
int topicLen;
unsigned int seqno; /* only used on restore */
} qEntry;
typedef struct
{
int type;
MQTTAsync_onSuccess* onSuccess;
MQTTAsync_onFailure* onFailure;
MQTTAsync_onSuccess5* onSuccess5;
MQTTAsync_onFailure5* onFailure5;
MQTTAsync_token token;
void* context;
START_TIME_TYPE start_time;
MQTTProperties properties;
union
{
struct
{
int count;
char** topics;
int* qoss;
MQTTSubscribe_options opts;
MQTTSubscribe_options* optlist;
} sub;
struct
{
int count;
char** topics;
} unsub;
struct
{
char* destinationName;
int payloadlen;
void* payload;
int qos;
int retained;
} pub;
struct
{
int internal;
int timeout;
enum MQTTReasonCodes reasonCode;
} dis;
struct
{
int currentURI;
int MQTTVersion; /**< current MQTT version being used to connect */
} conn;
} details;
} MQTTAsync_command;
typedef struct MQTTAsync_struct
{
char* serverURI;
int ssl;
int websocket;
Clients* c;
/* "Global", to the client, callback definitions */
MQTTAsync_connectionLost* cl;
MQTTAsync_messageArrived* ma;
MQTTAsync_deliveryComplete* dc;
void* clContext; /* the context to be associated with the conn lost callback*/
void* maContext; /* the context to be associated with the msg arrived callback*/
void* dcContext; /* the context to be associated with the deliv complete callback*/
MQTTAsync_connected* connected;
void* connected_context; /* the context to be associated with the connected callback*/
MQTTAsync_disconnected* disconnected;
void* disconnected_context; /* the context to be associated with the disconnected callback*/
MQTTAsync_updateConnectOptions* updateConnectOptions;
void* updateConnectOptions_context;
/* Each time connect is called, we store the options that were used. These are reused in
any call to reconnect, or an automatic reconnect attempt */
MQTTAsync_command connect; /* Connect operation properties */
MQTTAsync_command disconnect; /* Disconnect operation properties */
MQTTAsync_command* pending_write; /* Is there a socket write pending? */
List* responses;
unsigned int command_seqno;
MQTTPacket* pack;
/* added for offline buffering */
MQTTAsync_createOptions* createOptions;
int shouldBeConnected;
int noBufferedMessages; /* the current number of buffered (publish) messages for this client */
/* added for automatic reconnect */
int automaticReconnect;
int minRetryInterval;
int maxRetryInterval;
int serverURIcount;
char** serverURIs;
int connectTimeout;
int currentInterval;
int currentIntervalBase;
START_TIME_TYPE lastConnectionFailedTime;
int retrying;
int reconnectNow;
/* MQTT V5 properties */
MQTTProperties* connectProps;
MQTTProperties* willProps;
} MQTTAsyncs;
typedef struct
{
MQTTAsync_command command;
MQTTAsyncs* client;
unsigned int seqno; /* only used on restore */
int not_restored;
char* key; /* if not_restored, this holds the key */
} MQTTAsync_queuedCommand;
void MQTTAsync_lock_mutex(mutex_type amutex);
void MQTTAsync_unlock_mutex(mutex_type amutex);
void MQTTAsync_terminate(void);
#if !defined(NO_PERSISTENCE)
int MQTTAsync_restoreCommands(MQTTAsyncs* client);
#endif
int MQTTAsync_addCommand(MQTTAsync_queuedCommand* command, int command_size);
void MQTTAsync_emptyMessageQueue(Clients* client);
void MQTTAsync_freeResponses(MQTTAsyncs* m);
void MQTTAsync_freeCommands(MQTTAsyncs* m);
int MQTTAsync_unpersistCommandsAndMessages(Clients* c);
void MQTTAsync_closeSession(Clients* client, enum MQTTReasonCodes reasonCode, MQTTProperties* props);
int MQTTAsync_disconnect1(MQTTAsync handle, const MQTTAsync_disconnectOptions* options, int internal);
int MQTTAsync_assignMsgId(MQTTAsyncs* m);
int MQTTAsync_getNoBufferedMessages(MQTTAsyncs* m);
void MQTTAsync_writeContinue(SOCKET socket);
void MQTTAsync_writeComplete(SOCKET socket, int rc);
void setRetryLoopInterval(int keepalive);
void MQTTAsync_NULLPublishResponses(MQTTAsyncs* m);
void MQTTAsync_NULLPublishCommands(MQTTAsyncs* m);
#if defined(_WIN32) || defined(_WIN64)
#else
#define WINAPI
#endif
thread_return_type WINAPI MQTTAsync_sendThread(void* n);
thread_return_type WINAPI MQTTAsync_receiveThread(void* n);
#endif /* MQTTASYNCUTILS_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,277 @@
/*******************************************************************************
* 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 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;
/**
* A callback which is invoked just before a write to persistence. This can be
* used to transform the data, for instance to encrypt it.
* @param context The context as set in ::MQTTAsync_setBeforePersistenceWrite
* @param bufcount The number of buffers to write to the persistence store.
* @param buffers An array of pointers to the data buffers.
* @param buflens An array of lengths of the data buffers.
* @return Return 0 if the function completes successfully, otherwise non 0.
*/
typedef int MQTTPersistence_beforeWrite(void* context, int bufcount, char* buffers[], int buflens[]);
/**
* A callback which is invoked just after a read from persistence. This can be
* used to transform the data, for instance to decrypt it.
* @param context The context as set in ::MQTTAsync_setAfterPersistenceRead
* @param buffer The address of a pointer to a buffer.
* @param buflen The address of an int that is the length of the buffer.
* @return Return 0 if the function completes successfully, otherwise non 0.
*/
typedef int MQTTPersistence_afterRead(void* context, char** buffer, int* buflen);
#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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,271 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 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 */
uint8_t mask[4]; /**< the websockets mask the payload is masked with, if any */
} 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, PacketBuffers* buffers, 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, unsigned int anInt);
unsigned 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 */

View File

@@ -0,0 +1,473 @@
/*******************************************************************************
* Copyright (c) 2009, 2021 IBM Corp. and Ian Craggs
*
* 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;
if (datalen < 2) /* enough data for connect flags and reason code? */
{
free(pack);
pack = NULL;
goto exit;
}
pack->flags.all = readChar(&curdata); /* connect flags */
pack->rc = readChar(&curdata); /* reason code */
if (MQTTVersion >= MQTTVERSION_5 && 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;
if (enddata - curdata < 2) /* Is there enough data to read the msgid? */
{
free(pack);
pack = NULL;
goto exit;
}
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);
ListFree(pack->qoss);
free(pack);
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;
if (enddata - curdata < 2) /* Is there enough data? */
{
free(pack);
pack = NULL;
goto exit;
}
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;
}

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

View File

@@ -0,0 +1,921 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 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"
#if defined(_WIN32) || defined(_WIN64)
#define snprintf _snprintf
#endif
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 )
pcontext = "."; /* working directory */
if ((per->context = malloc(strlen(pcontext) + 1)) == NULL)
{
free(per);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
strcpy(per->context, pcontext);
/* 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_restorePackets(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 !defined(NO_PERSISTENCE)
if (c->persistence != NULL)
{
rc = c->persistence->pclose(c->phandle);
if (c->persistence->popen == pstopen) {
if (c->persistence->context)
free(c->persistence->context);
free(c->persistence);
}
c->phandle = NULL;
c->persistence = NULL;
}
#endif
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_restorePackets(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 &&
(c->afterRead == NULL || (rc = c->afterRead(c->afterRead_context, &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;
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
char *key = malloc(keysize);
int chars = 0;
if (!key)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
publish->MQTTVersion = c->MQTTVersion;
if (publish->MQTTVersion >= MQTTVERSION_5)
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBREL, publish->msgId);
else
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBREL, publish->msgId);
if (chars >= keysize)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
}
else
{
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);
msgs_sent++;
}
free(key);
}
else if (strncmp(cur_key, PERSISTENCE_PUBREL, strlen(PERSISTENCE_PUBREL)) == 0)
{
/* orphaned PUBRELs ? */
Pubrel* pubrel = (Pubrel*)pack;
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
char *key = malloc(keysize);
int chars = 0;
if (!key)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
pubrel->MQTTVersion = c->MQTTVersion;
if (pubrel->MQTTVersion >= MQTTVERSION_5)
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, pubrel->msgId);
else
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_SENT, pubrel->msgId);
if (chars >= keysize)
{
rc = MQTTCLIENT_PERSISTENCE_ERROR;
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
}
else 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]);
msgkeys[i] = NULL;
}
i++;
}
}
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:
if (msgkeys)
{
int i = 0;
for (i = 0; i < nkeys; ++i)
{
if (msgkeys[i])
free(msgkeys[i]);
}
free(msgkeys);
}
if (buffer)
free(buffer);
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_putPacket(SOCKET 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)
{
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
if ((key = malloc(keysize)) == 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;
}
if (snprintf(key, keysize, "%s%d", key_id, msgId) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
else if (scr == 1) /* receiving PUBLISH QoS2 */
{
char* key_id = PERSISTENCE_PUBLISH_RECEIVED;
if (MQTTVersion >= MQTTVERSION_5)
key_id = PERSISTENCE_V5_PUBLISH_RECEIVED;
if (snprintf(key, keysize, "%s%d", key_id, msgId) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
if (rc == 0 && client->beforeWrite)
rc = client->beforeWrite(client->beforeWrite_context, nbufs, bufs, lens);
if (rc == 0)
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)
{
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
char *key = malloc(keysize);
int chars = 0;
if (!key)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
if (strcmp(type, PERSISTENCE_PUBLISH_SENT) == 0 ||
strcmp(type, PERSISTENCE_V5_PUBLISH_SENT) == 0)
{
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, msgId)) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
else
{
rc = c->persistence->premove(c->phandle, key);
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBREL, msgId)) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
else
{
rc += c->persistence->premove(c->phandle, key);
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_SENT, msgId)) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
else
{
rc += c->persistence->premove(c->phandle, key);
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBREL, msgId)) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
else
rc += c->persistence->premove(c->phandle, key);
}
}
}
}
else /* PERSISTENCE_PUBLISH_SENT && qos == 1 */
{ /* or PERSISTENCE_PUBLISH_RECEIVED */
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_RECEIVED, msgId)) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
else
{
rc = c->persistence->premove(c->phandle, key);
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_RECEIVED, msgId)) >= keysize)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
else
rc += c->persistence->premove(c->phandle, key);
}
}
if (rc == MQTTCLIENT_PERSISTENCE_ERROR)
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
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;
#if defined(_WIN32) || defined(_WIN64)
#define KEYSIZE PERSISTENCE_MAX_KEY_LENGTH + 1
#else
const size_t KEYSIZE = PERSISTENCE_MAX_KEY_LENGTH + 1;
#endif
char key[KEYSIZE];
int chars = 0;
FUNC_ENTRY;
if (client->MQTTVersion >= MQTTVERSION_5)
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_V5_QUEUE_KEY, qe->seqno);
else
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_QUEUE_KEY, qe->seqno);
if (chars >= KEYSIZE)
{
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
rc = MQTTCLIENT_PERSISTENCE_ERROR;
}
else 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;
#if !defined(_WIN32) && !defined(_WIN64)
const size_t KEYSIZE = PERSISTENCE_MAX_KEY_LENGTH + 1;
#endif
char key[KEYSIZE];
int chars = 0;
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->qentry_seqno == PERSISTENCE_SEQNO_LIMIT)
aclient->qentry_seqno = 0;
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;
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_V5_QUEUE_KEY, aclient->qentry_seqno);
}
else
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_QUEUE_KEY, aclient->qentry_seqno);
if (chars >= KEYSIZE)
rc = MQTTCLIENT_PERSISTENCE_ERROR;
else
{
qe->seqno = aclient->qentry_seqno;
if (aclient->beforeWrite)
rc = aclient->beforeWrite(aclient->beforeWrite_context, bufindex, (char**)bufs, lens);
if (rc == 0 && (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 &&
(c->afterRead == NULL || (rc = c->afterRead(c->afterRead_context, &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));
c->qentry_seqno = max(c->qentry_seqno, qe->seqno);
entries_restored++;
}
if (buffer)
free(buffer);
}
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,99 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 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 client incoming message queue */
#define PERSISTENCE_QUEUE_KEY "q-"
/** Stem of the key for an MQTT V5 incoming message queue */
#define PERSISTENCE_V5_QUEUE_KEY "q5-"
/** Maximum length of a stem for a persistence key */
#define PERSISTENCE_MAX_STEM_LENGTH 4
/** Maximum allowed length of a persistence key */
#define PERSISTENCE_MAX_KEY_LENGTH 10
/** Maximum size of an integer sequence number appended to a persistence key */
#define PERSISTENCE_SEQNO_LIMIT 1000000 /*10^(PERSISTENCE_MAX_KEY_LENGTH - PERSISTENCE_MAX_STEM_LENGTH)*/
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_restorePackets(Clients* c);
void* MQTTPersistence_restorePacket(int MQTTVersion, char* buffer, size_t buflen);
void MQTTPersistence_insertInOrder(List* list, void* content, size_t size);
int MQTTPersistence_putPacket(SOCKET 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* 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
/** 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

View File

@@ -0,0 +1,552 @@
/*******************************************************************************
* 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 = -1;
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:
if ((len = MQTTLenStringRead(&prop->value.data, pptr, enddata)) == -1)
break; /* error */
if ((prop->value.data.data = datadup(&prop->value.data)) == NULL)
{
len = -1;
break; /* error */
}
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
{
int proplen = MQTTLenStringRead(&prop->value.value, pptr, enddata);
if (proplen == -1)
{
len = -1;
free(prop->value.data.data);
break;
}
len += proplen;
if ((prop->value.value.data = datadup(&prop->value.value)) == NULL)
{
len = -1;
free(prop->value.data.data);
break;
}
}
break;
}
}
return (len == -1) ? -1 : 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? */
{
int proplen = 0;
*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;
}
if ((proplen = MQTTProperty_read(&properties->array[properties->count], pptr, enddata)) > 0)
remlength -= proplen;
else
break;
properties->count++;
}
if (remlength == 0)
rc = 1; /* data read successfully */
}
if (rc != 1 && properties->array != NULL)
MQTTProperties_free(properties);
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,222 @@
/*******************************************************************************
* Copyright (c) 2017, 2023 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. Memory is allocated in this function,
* so MQTTClient_create or MQTTAsync_create must be called first to initialize the
* internal heap tracking. Alternatively MQTTAsync_global_init() can be called first
* or build with the HIGH_PERFORMANCE option which disables the heap tracking.
* @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 */

View File

@@ -0,0 +1,46 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs
*
* 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
{
SOCKET 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 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, SOCKET sock);
int MQTTProtocol_handlePubacks(void* pack, SOCKET sock, Publications** pubToRemove);
int MQTTProtocol_handlePubrecs(void* pack, SOCKET sock, Publications** pubToRemove);
int MQTTProtocol_handlePubrels(void* pack, SOCKET sock);
int MQTTProtocol_handlePubcomps(void* pack, SOCKET sock, Publications** pubToRemove);
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);
void MQTTProtocol_writeAvailable(SOCKET socket);
//#define MQTTStrdup(src) MQTTStrncpy(malloc(strlen(src)+1), src, strlen(src)+1)
#endif

View File

@@ -0,0 +1,479 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 IBM Corp., Ian Craggs 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, 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
* Sven Gambel - add generic proxy 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 "Proxy.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, int default_port)
{
char* buf = (char*)uri;
char* colon_pos;
size_t len;
char* topic_pos;
FUNC_ENTRY;
colon_pos = strrchr(uri, ':'); /* reverse find to allow for ':' in IPv6 addresses */
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;
}
/* find any topic portion */
topic_pos = (char*)uri;
if (colon_pos)
topic_pos = colon_pos;
topic_pos = strchr(topic_pos, '/');
if (topic_pos)
{
if (topic)
*topic = topic_pos;
if (!colon_pos)
len = topic_pos - uri;
}
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 character. This will avoid problems when a user code or a password
* contains a '@' or another special character ('%' included)
* @param p0 output string
* @param p1 input string
* @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;
}
/*
* Examples of proxy settings:
* http://your.proxy.server:8080/
* http://user:pass@my.proxy.server:8080/
*/
int MQTTProtocol_setHTTPProxy(Clients* aClient, char* source, char** dest, char** auth_dest, char* prefix)
{
b64_size_t basic_auth_in_len, basic_auth_out_len;
b64_data_t *basic_auth;
char *p1;
int rc = 0;
if (*auth_dest)
{
free(*auth_dest);
*auth_dest = NULL;
}
if (source)
{
if ((p1 = strstr(source, prefix)) != NULL) /* skip http:// prefix, if any */
source += strlen(prefix);
*dest = source;
if ((p1 = strchr(source, '@')) != NULL) /* find user.pass separator */
*dest = p1 + 1;
if (p1)
{
/* basic auth len is string between http:// and @ */
basic_auth_in_len = (b64_size_t)(p1 - source);
if (basic_auth_in_len > 0)
{
basic_auth = (b64_data_t *)malloc(sizeof(char)*(basic_auth_in_len+1));
if (!basic_auth)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
MQTTProtocol_specialChars((char*)basic_auth, source, &basic_auth_in_len);
basic_auth_out_len = Base64_encodeLength(basic_auth, basic_auth_in_len) + 1; /* add 1 for trailing NULL */
if ((*auth_dest = (char *)malloc(sizeof(char)*basic_auth_out_len)) == NULL)
{
free(basic_auth);
rc = PAHO_MEMORY_ERROR;
goto exit;
}
Base64_encode(*auth_dest, basic_auth_out_len, basic_auth, basic_auth_in_len);
free(basic_auth);
}
}
}
exit:
return rc;
}
/**
* 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 = 0,
port;
size_t addr_len;
char* p0;
FUNC_ENTRY;
aClient->good = 1;
if (aClient->httpProxy)
p0 = aClient->httpProxy;
else
p0 = getenv("http_proxy");
if (p0)
{
if ((rc = MQTTProtocol_setHTTPProxy(aClient, p0, &aClient->net.http_proxy, &aClient->net.http_proxy_auth, "http://")) != 0)
goto exit;
Log(TRACE_PROTOCOL, -1, "Setting http proxy to %s", aClient->net.http_proxy);
if (aClient->net.http_proxy_auth)
Log(TRACE_PROTOCOL, -1, "Setting http proxy auth to %s", aClient->net.http_proxy_auth);
}
#if defined(OPENSSL)
if (aClient->httpsProxy)
p0 = aClient->httpsProxy;
else
p0 = getenv("https_proxy");
if (p0)
{
if ((rc = MQTTProtocol_setHTTPProxy(aClient, p0, &aClient->net.https_proxy, &aClient->net.https_proxy_auth, "https://")) != 0)
goto exit;
Log(TRACE_PROTOCOL, -1, "Setting https proxy to %s", aClient->net.https_proxy);
if (aClient->net.https_proxy_auth)
Log(TRACE_PROTOCOL, -1, "Setting https proxy auth to %s", aClient->net.https_proxy_auth);
}
if (!ssl && aClient->net.http_proxy) {
#else
if (aClient->net.http_proxy) {
#endif
addr_len = MQTTProtocol_addressPort(aClient->net.http_proxy, &port, NULL, PROXY_DEFAULT_PORT);
#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 && aClient->net.https_proxy) {
addr_len = MQTTProtocol_addressPort(aClient->net.https_proxy, &port, NULL, PROXY_DEFAULT_PORT);
#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 {
#if defined(OPENSSL)
addr_len = MQTTProtocol_addressPort(ip_address, &port, NULL, ssl ?
(websocket ? WSS_DEFAULT_PORT : SECURE_MQTT_DEFAULT_PORT) :
(websocket ? WS_DEFAULT_PORT : MQTT_DEFAULT_PORT) );
#else
addr_len = MQTTProtocol_addressPort(ip_address, &port, NULL, websocket ? WS_DEFAULT_PORT : MQTT_DEFAULT_PORT);
#endif
#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 (aClient->net.https_proxy) {
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
rc = 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 (aClient->net.http_proxy) {
#else
if (aClient->net.http_proxy) {
#endif
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
rc = Proxy_connect( &aClient->net, 0, ip_address);
}
if ( websocket )
{
#if defined(OPENSSL)
rc = WebSocket_connect(&aClient->net, ssl, ip_address);
#else
rc = WebSocket_connect(&aClient->net, 0, ip_address);
#endif
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, SOCKET 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, SOCKET 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, SOCKET 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;
}
/**
* Process an incoming disconnect packet for a socket
* @param pack pointer to the disconnect packet
* @param sock the socket on which the packet was received
* @return completion code
*/
int MQTTProtocol_handleDisconnects(void* pack, SOCKET sock)
{
Ack* disconnect = (Ack*)pack;
Clients* client = NULL;
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
Log(LOG_PROTOCOL, 30, NULL, sock, client->clientID, disconnect->rc);
MQTTPacket_freeAck(disconnect);
FUNC_EXIT_RC(rc);
return rc;
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs, 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, Allan Stockdill-Mander - SSL updates
* Ian Craggs - MQTT 3.1.1 support
* Ian Craggs - SNI support
* Ian Craggs - MQTT 5.0 support
* Sven Gambel - add generic proxy 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 MQTT_DEFAULT_PORT 1883
#define SECURE_MQTT_DEFAULT_PORT 8883
#define WS_DEFAULT_PORT 80
#define WSS_DEFAULT_PORT 443
#define PROXY_DEFAULT_PORT 8080
size_t MQTTProtocol_addressPort(const char* uri, int* port, const char **topic, int default_port);
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, SOCKET sock);
int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss, int msgID,
MQTTSubscribe_options* opts, MQTTProperties* props);
int MQTTProtocol_handleSubacks(void* pack, SOCKET sock);
int MQTTProtocol_unsubscribe(Clients* client, List* topics, int msgID, MQTTProperties* props);
int MQTTProtocol_handleUnsubacks(void* pack, SOCKET sock);
int MQTTProtocol_handleDisconnects(void* pack, SOCKET sock);
#endif

View File

@@ -0,0 +1,101 @@
/*******************************************************************************
* 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 "MQTTPacket.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;
}

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

View File

@@ -0,0 +1,112 @@
/*******************************************************************************
* Copyright (c) 2020, 2021 IBM Corp. and Ian Craggs
*
* 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(ELAPSED_TIME_TYPE milliseconds)
{
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
Sleep((DWORD)milliseconds);
#else
usleep((useconds_t)(milliseconds*1000));
#endif
FUNC_EXIT;
}
#if defined(_WIN32) || defined(_WIN64)
START_TIME_TYPE MQTTTime_start_clock(void)
{
#if WINVER >= _WIN32_WINNT_VISTA
return GetTickCount64();
#else
return GetTickCount();
#endif
}
#elif defined(AIX)
START_TIME_TYPE MQTTTime_start_clock(void)
{
struct timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);
return start;
}
#else
START_TIME_TYPE MQTTTime_start_clock(void)
{
struct timeval start;
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)
/*
* @param t_new most recent time in milliseconds from GetTickCount()
* @param t_old older time in milliseconds from GetTickCount()
* @return difference in milliseconds
*/
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old)
{
#if WINVER >= _WIN32_WINNT_VISTA
return (DIFF_TIME_TYPE)(t_new - t_old);
#else
if (t_old < t_new) /* check for wrap around condition in GetTickCount */
return (DIFF_TIME_TYPE)(t_new - t_old);
else
return (DIFF_TIME_TYPE)((0xFFFFFFFFL - t_old) + 1 + t_new);
#endif
}
#elif defined(AIX)
#define assert(a)
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old)
{
struct timespec result;
ntimersub(t_new, t_old, result);
return (DIFF_TIME_TYPE)((result.tv_sec)*1000L + (result.tv_nsec)/1000000L); /* convert to milliseconds */
}
#else
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old)
{
struct timeval result;
timersub(&t_new, &t_old, &result);
return (DIFF_TIME_TYPE)(((DIFF_TIME_TYPE)result.tv_sec)*1000 + ((DIFF_TIME_TYPE)result.tv_usec)/1000); /* convert to milliseconds */
}
#endif
ELAPSED_TIME_TYPE MQTTTime_elapsed(START_TIME_TYPE milliseconds)
{
return (ELAPSED_TIME_TYPE)MQTTTime_difftime(MQTTTime_now(), milliseconds);
}

View File

@@ -0,0 +1,49 @@
/*******************************************************************************
* 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
#include <stdint.h>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#if WINVER >= _WIN32_WINNT_VISTA
#define START_TIME_TYPE ULONGLONG
#define START_TIME_ZERO 0
#else
#define START_TIME_TYPE DWORD
#define START_TIME_ZERO 0
#endif
#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
#define ELAPSED_TIME_TYPE uint64_t
#define DIFF_TIME_TYPE int64_t
void MQTTTime_sleep(ELAPSED_TIME_TYPE milliseconds);
START_TIME_TYPE MQTTTime_start_clock(void);
START_TIME_TYPE MQTTTime_now(void);
ELAPSED_TIME_TYPE MQTTTime_elapsed(START_TIME_TYPE milliseconds);
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old);
#endif

View File

@@ -0,0 +1,231 @@
/*******************************************************************************
* Copyright (c) 2012, 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 <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>
#define snprintf _snprintf
#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__)
snprintf(namebuf, sizeof(namebuf), "cyg%s-1.dll", libraries[i]);
#elif defined(_WIN32) || defined(_WIN64)
snprintf(namebuf, sizeof(namebuf), "%s.dll", libraries[i]);
#elif defined(OSX)
snprintf(namebuf, sizeof(namebuf), "lib%s.1.dylib", libraries[i]);
#else
snprintf(namebuf, sizeof(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) */

View File

@@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2009, 2021 IBM Corp., Ian Craggs
*
* 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 */
"%d %s <- DISCONNECT (%d)", /* 30 */
};
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;
}

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

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) */

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 */

View File

@@ -0,0 +1,154 @@
/*******************************************************************************
* Copyright (c) 2009, 2021 Diehl Metering.
*
* 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
* Sven Gambel - move WebSocket proxy support to generic proxy support
*******************************************************************************/
#include <stdio.h>
#include <string.h>
// for timeout process in Proxy_connect()
#include <time.h>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
#else
#include <unistd.h>
#endif
#include "Log.h"
#include "MQTTProtocolOut.h"
#include "StackTrace.h"
#include "Heap.h"
#if defined(OPENSSL)
#include "SSLSocket.h"
#include <openssl/rand.h>
#endif /* defined(OPENSSL) */
#include "Socket.h"
/**
* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint.
*
* @param[in] net network connection to proxy.
* @param[in] ssl enable ssl.
* @param[in] hostname hostname of endpoint.
*
* @retval SOCKET_ERROR failed to network connection
* @retval 0 connection to endpoint
*
*/
int Proxy_connect(networkHandles *net, int ssl, const char *hostname)
{
int port, i, rc = 0, buf_len=0;
char *buf = NULL;
size_t hostname_len, actual_len = 0;
time_t current, timeout;
PacketBuffers nulbufs = {0, NULL, NULL, NULL, {0, 0, 0, 0}};
FUNC_ENTRY;
hostname_len = MQTTProtocol_addressPort(hostname, &port, NULL, PROXY_DEFAULT_PORT);
for ( i = 0; i < 2; ++i ) {
#if defined(OPENSSL)
if(ssl) {
if (net->https_proxy_auth) {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"Proxy-authorization: Basic %s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname, net->https_proxy_auth);
}
else {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname);
}
}
else {
#endif
if (net->http_proxy_auth) {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"Proxy-authorization: Basic %s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname, net->http_proxy_auth);
}
else {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname);
}
#if defined(OPENSSL)
}
#endif
if ( i==0 && buf_len > 0 ) {
++buf_len;
if ((buf = malloc( buf_len )) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
}
}
Log(TRACE_PROTOCOL, -1, "Proxy_connect: \"%s\"", buf);
Socket_putdatas(net->socket, buf, buf_len, nulbufs);
free(buf);
buf = NULL;
time(&timeout);
timeout += (time_t)10;
while(1) {
buf = Socket_getdata(net->socket, (size_t)12, &actual_len, &rc);
if(actual_len) {
if ( (strncmp( buf, "HTTP/1.0 200", 12 ) != 0) && (strncmp( buf, "HTTP/1.1 200", 12 ) != 0) )
rc = SOCKET_ERROR;
break;
}
else {
time(&current);
if(current > timeout) {
rc = SOCKET_ERROR;
break;
}
#if defined(_WIN32) || defined(_WIN64)
Sleep(250);
#else
usleep(250000);
#endif
}
}
/* flush the SocketBuffer */
actual_len = 1;
while (actual_len)
{
int rc1;
buf = Socket_getdata(net->socket, (size_t)1, &actual_len, &rc1);
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2009, 2021 Diehl Metering.
*
* 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:
* Sven Gambel - move WebSocket proxy support to generic proxy support
*******************************************************************************/
#if !defined(PROXY_H)
#define PROXY_H
#include "Clients.h"
/* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint */
int Proxy_connect(networkHandles *net, int ssl, const char *hostname );
#endif /* PROXY_H */

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_mqtt(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_mqtt(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_mqtt(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_mqtt(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_mqtt( &c );
r[1] = SHA1_Update_mqtt( &c, test_data[i].in, strlen(test_data[i].in));
r[2] = SHA1_Final_mqtt( 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) */

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_mqtt
* @see SHA1_Final
*/
int SHA1_Init_mqtt(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_mqtt
* @see SHA1_Final
*/
int SHA1_Update_mqtt(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_mqtt
* @see SHA1_Final
*/
int SHA1_Final_mqtt(unsigned char *md, SHA_CTX *ctx);
#endif /* if defined(OPENSSL) */
#endif /* SHA1_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs
*
* 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://"
#define URI_MQTTS "mqtts://"
/** 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, SOCKET socket, char* c);
char *SSLSocket_getdata(SSL* ssl, SOCKET socket, size_t bytes, size_t* actual_len, int* rc);
int SSLSocket_close(networkHandles* net);
int SSLSocket_putdatas(SSL* ssl, SOCKET socket, char* buf0, size_t buf0len, PacketBuffers bufs);
int SSLSocket_connect(SSL* ssl, SOCKET sock, const char* hostname, int verify, int (*cb)(const char *str, size_t len, void *u), void* u);
SOCKET SSLSocket_getPendingRead(void);
int SSLSocket_continueWrite(pending_writes* pw);
int SSLSocket_abortWrite(pending_writes* pw);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs 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 implementation and documentation
* Ian Craggs - async client updates
*******************************************************************************/
#if !defined(SOCKET_H)
#define SOCKET_H
#include <stdint.h>
#include <sys/types.h>
#if defined(_WIN32) || defined(_WIN64)
#include <errno.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define MAXHOSTNAMELEN 256
#define poll WSAPoll
#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 <poll.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
#define SOCKET int
#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"
/*
* Network write buffers for an MQTT packet
*/
typedef struct
{
int count; /**> number of buffers/buflens/frees */
char** buffers; /**> array of byte buffers */
size_t* buflens; /**> array of lengths of buffers */
int* frees; /**> array of flags indicating whether each buffer needs to be freed */
uint8_t mask[4]; /**> websocket mask used to mask the buffer data, if any */
} PacketBuffers;
/**
* Structure to hold all socket data for the module
*/
typedef struct
{
List* connect_pending; /**< list of sockets for which a connect is pending */
List* write_pending; /**< list of sockets for which a write is pending */
#if defined(USE_SELECT)
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) */
fd_set pending_wset; /**< socket pending write set for select */
#else
unsigned int nfds; /**< no of file descriptors for poll */
struct pollfd* fds_read; /**< poll read file descriptors */
struct pollfd* fds_write;
struct {
int cur_fd; /**< index into the fds_saved array */
unsigned int nfds; /**< number of fds in the fds_saved array */
struct pollfd* fds_write;
struct pollfd* fds_read;
} saved;
#endif
} Sockets;
void Socket_outInitialize(void);
void Socket_outTerminate(void);
SOCKET Socket_getReadySocket(int more_work, int timeout, mutex_type mutex, int* rc);
int Socket_getch(SOCKET socket, char* c);
char *Socket_getdata(SOCKET socket, size_t bytes, size_t* actual_len, int* rc);
int Socket_putdatas(SOCKET socket, char* buf0, size_t buf0len, PacketBuffers bufs);
int Socket_close(SOCKET 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, SOCKET* socket, long timeout);
#else
int Socket_new(const char* addr, size_t addr_len, int port, SOCKET* socket);
#endif
int Socket_noPendingWrites(SOCKET socket);
char* Socket_getpeer(SOCKET sock);
void Socket_addPendingWrite(SOCKET socket);
void Socket_clearPendingWrite(SOCKET socket);
typedef void Socket_writeContinue(SOCKET socket);
void Socket_setWriteContinueCallback(Socket_writeContinue*);
typedef void Socket_writeComplete(SOCKET socket, int rc);
void Socket_setWriteCompleteCallback(Socket_writeComplete*);
typedef void Socket_writeAvailable(SOCKET socket);
void Socket_setWriteAvailableCallback(Socket_writeAvailable*);
#endif /* SOCKET_H */

View File

@@ -0,0 +1,448 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs 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, 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(SOCKET 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(SOCKET 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);
if (newmem)
{
memcpy(newmem, queue->buf, queue->datalen);
free(queue->buf);
queue->buf = newmem;
}
else
{
free(queue->buf);
queue->buf = NULL;
goto exit;
}
}
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(SOCKET 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(SOCKET 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(SOCKET 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(SOCKET 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(SOCKET socket, SSL* ssl, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes)
#else
int SocketBuffer_pendingWrite(SOCKET 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(SOCKET 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(SOCKET 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(SOCKET 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;
}

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs 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, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
#if !defined(SOCKETBUFFER_H)
#define SOCKETBUFFER_H
#include "Socket.h"
#if defined(OPENSSL)
#include <openssl/ssl.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
typedef WSABUF iobuf;
#else
typedef struct iovec iobuf;
#endif
typedef struct
{
SOCKET 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
{
SOCKET socket;
int 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(SOCKET socket);
char* SocketBuffer_getQueuedData(SOCKET socket, size_t bytes, size_t* actual_len);
int SocketBuffer_getQueuedChar(SOCKET socket, char* c);
void SocketBuffer_interrupted(SOCKET socket, size_t actual_len);
char* SocketBuffer_complete(SOCKET socket);
void SocketBuffer_queueChar(SOCKET socket, char c);
#if defined(OPENSSL)
int SocketBuffer_pendingWrite(SOCKET socket, SSL* ssl, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes);
#else
int SocketBuffer_pendingWrite(SOCKET socket, int count, iobuf* iovecs, int* frees, size_t total, size_t bytes);
#endif
pending_writes* SocketBuffer_getWrite(SOCKET socket);
int SocketBuffer_writeComplete(SOCKET socket);
pending_writes* SocketBuffer_updateWrite(SOCKET socket, char* topic, char* payload);
#endif

View File

@@ -0,0 +1,208 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 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 = Paho_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)
{
Paho_thread_lock_mutex(stack_mutex);
if (!setStack(1))
goto exit;
if (trace_level != -1)
Log_stackTrace(trace_level, 9, 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:
Paho_thread_unlock_mutex(stack_mutex);
}
void StackTrace_exit(const char* name, int line, void* rc, enum LOG_LEVELS trace_level)
{
Paho_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, my_thread->id, my_thread->current_depth, name, line, NULL);
else
Log_stackTrace(trace_level, 11, my_thread->id, my_thread->current_depth, name, line, (int*)rc);
}
exit:
Paho_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

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_ */

View File

@@ -0,0 +1,692 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 IBM Corp. and Ian Craggs
*
* 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
*/
void Paho_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);
CloseHandle(thread);
#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;
}
int Thread_set_name(const char* thread_name)
{
int rc = 0;
#if defined(_WIN32) || defined(_WIN64)
#define MAX_THREAD_NAME_LENGTH 30
wchar_t wchars[MAX_THREAD_NAME_LENGTH];
#endif
FUNC_ENTRY;
#if defined(_WIN32) || defined(_WIN64)
/* Using NTDDI_VERSION rather than WINVER for more detailed version targeting */
/* Can't get this conditional compilation to work so remove it for now */
/*#if NTDDI_VERSION >= NTDDI_WIN10_RS1
mbstowcs(wchars, thread_name, MAX_THREAD_NAME_LENGTH);
rc = (int)SetThreadDescription(GetCurrentThread(), wchars);
#endif*/
#elif defined(OSX)
/* pthread_setname_np __API_AVAILABLE(macos(10.6), ios(3.2)) */
#if defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
rc = pthread_setname_np(thread_name);
#endif
#else
#if defined(__GNUC__) && defined(__linux__)
#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 12
rc = pthread_setname_np(Paho_thread_getid(), thread_name);
#endif
#endif
#endif
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Create a new mutex
* @param rc return code: 0 for success, negative otherwise
* @return the new mutex
*/
mutex_type Paho_thread_create_mutex(int* rc)
{
mutex_type mutex = NULL;
FUNC_ENTRY;
*rc = -1;
#if defined(_WIN32) || defined(_WIN64)
mutex = CreateMutex(NULL, 0, NULL);
*rc = (mutex == NULL) ? GetLastError() : 0;
#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 Paho_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 Paho_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 Paho_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 Paho_thread_getid(void)
{
#if defined(_WIN32) || defined(_WIN64)
return GetCurrentThreadId();
#else
return pthread_self();
#endif
}
/**
* Create a new semaphore
* @param rc return code: 0 for success, negative otherwise
* @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 */
);
*rc = (sem == NULL) ? GetLastError() : 0;
#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 (ms) for condition variable
* @return 0 for success, ETIMEDOUT otherwise
*/
int Thread_wait_cond(cond_type condvar, int timeout_ms)
{
int rc = 0;
struct timespec cond_timeout;
struct timespec interval;
FUNC_ENTRY;
interval.tv_sec = timeout_ms / 1000;
interval.tv_nsec = (timeout_ms % 1000) * 1000000L;
#if defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 /* for older versions of MacOS */
struct timeval cur_time;
gettimeofday(&cur_time, NULL);
cond_timeout.tv_sec = cur_time.tv_sec;
cond_timeout.tv_nsec = cur_time.tv_usec * 1000;
#else
clock_gettime(CLOCK_REALTIME, &cond_timeout);
#endif
cond_timeout.tv_sec += interval.tv_sec;
cond_timeout.tv_nsec += (timeout_ms % 1000) * 1000000L;
if (cond_timeout.tv_nsec >= 1000000000L)
{
cond_timeout.tv_sec++;
cond_timeout.tv_nsec += (cond_timeout.tv_nsec - 1000000000L);
}
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();
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_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();
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_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

View File

@@ -0,0 +1,88 @@
/*******************************************************************************
* Copyright (c) 2009, 2023 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
#if !defined(_WIN32) && !defined(_WIN64)
#if defined(__GNUC__) && defined(__linux__)
#if !defined(_GNU_SOURCE)
// for pthread_setname
#define _GNU_SOURCE
#endif
#endif
#endif
#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 void Paho_thread_start(thread_fn, void*);
int Thread_set_name(const char* thread_name);
LIBMQTT_API mutex_type Paho_thread_create_mutex(int*);
LIBMQTT_API int Paho_thread_lock_mutex(mutex_type);
LIBMQTT_API int Paho_thread_unlock_mutex(mutex_type);
int Paho_thread_destroy_mutex(mutex_type);
LIBMQTT_API thread_id_type Paho_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

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

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

View File

@@ -0,0 +1,7 @@
#ifndef VERSIONINFO_H
#define VERSIONINFO_H
#define BUILD_TIMESTAMP "@BUILD_TIMESTAMP@"
#define CLIENT_VERSION "@CLIENT_VERSION@"
#endif /* VERSIONINFO_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (c) 2018, 2022 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
* Sven Gambel - move WebSocket proxy support to generic proxy support
*******************************************************************************/
#if !defined(WEBSOCKET_H)
#define WEBSOCKET_H
#include "MQTTPacket.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, int ssl, 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);
size_t WebSocket_framePos();
void WebSocket_framePosSeekTo(size_t);
/* send data out, in websocket format only if required */
int WebSocket_putdatas(networkHandles* net, char** buf0, size_t* buf0len, PacketBuffers* bufs);
/* releases any resources used by the websocket system */
void WebSocket_terminate(void);
/* handles websocket upgrade request */
int WebSocket_upgrade(networkHandles *net);
#endif /* WEBSOCKET_H */

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_ */

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

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

View File

@@ -0,0 +1,12 @@
project(test_package C)
cmake_minimum_required(VERSION 2.8.11)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
file(GLOB SOURCE_FILES *.c)
message("LIBS: ${CONAN_LIBS}")
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})

View File

@@ -0,0 +1,26 @@
from conans import ConanFile, CMake, tools, RunEnvironment
import os
class TestPackageConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake"
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def imports(self):
self.copy("*paho*.dll", dst="bin", src="bin")
self.copy("*paho*.dylib*", dst="bin", src="lib")
def test(self):
with tools.environment_append(RunEnvironment(self).vars):
bin_path = os.path.join("bin", "test_package")
if self.settings.os == "Windows":
self.run(bin_path)
elif self.settings.os == "Macos":
self.run("DYLD_LIBRARY_PATH=%s %s" % (os.environ.get('DYLD_LIBRARY_PATH', ''), bin_path))
else:
self.run("LD_LIBRARY_PATH=%s %s" % (os.environ.get('LD_LIBRARY_PATH', ''), bin_path))

View File

@@ -0,0 +1,58 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#define ADDRESS "tcp://localhost:1883"
#define CLIENTID "ExampleClientSub"
#define TOPIC "MQTT Examples"
#define PAYLOAD "Hello World!"
#define QOS 1
#define TIMEOUT 10000L
volatile MQTTClient_deliveryToken deliveredtoken;
void delivered(void *context, MQTTClient_deliveryToken dt)
{
printf("Message with token value %d delivery confirmed\n", dt);
deliveredtoken = dt;
}
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
int i;
char *payloadptr;
printf("Message arrived\n");
printf(" topic: %s\n", topicName);
printf(" message: ");
payloadptr = message->payload;
for (i = 0; i < message->payloadlen; i++)
{
putchar(*payloadptr++);
}
putchar('\n');
MQTTClient_freeMessage(&message);
MQTTClient_free(topicName);
return 1;
}
void connlost(void *context, char *cause)
{
printf("\nConnection lost\n");
printf(" cause: %s\n", cause);
}
int main(int argc, char *argv[])
{
printf("\nCreating MQTTClient\n");
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
MQTTClient_destroy(&client);
printf("\nExiting\n");
return 0;
}

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1 @@
3

View File

@@ -0,0 +1 @@
13

View File

@@ -0,0 +1,51 @@
#*******************************************************************************
# Copyright (c) 2016-2024
#
# 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
# http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Guilherme Maciel Ferreira - initial version
# Frank Pagliughi
#*******************************************************************************/
install(
FILES
async_client.h
buffer_ref.h
buffer_view.h
callback.h
client.h
connect_options.h
create_options.h
delivery_token.h
disconnect_options.h
exception.h
export.h
iaction_listener.h
iasync_client.h
iclient_persistence.h
message.h
platform.h
properties.h
response_options.h
server_response.h
ssl_options.h
string_collection.h
subscribe_options.h
thread_queue.h
token.h
topic_matcher.h
topic.h
types.h
will_options.h
DESTINATION
include/mqtt
)

View File

@@ -0,0 +1,820 @@
/////////////////////////////////////////////////////////////////////////////
/// @file async_client.h
/// Declaration of MQTT async_client class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
* Frank Pagliughi - MQTT v5 support
*******************************************************************************/
#ifndef __mqtt_async_client_h
#define __mqtt_async_client_h
#include "MQTTAsync.h"
#include "mqtt/types.h"
#include "mqtt/token.h"
#include "mqtt/create_options.h"
#include "mqtt/string_collection.h"
#include "mqtt/delivery_token.h"
#include "mqtt/iclient_persistence.h"
#include "mqtt/iaction_listener.h"
#include "mqtt/properties.h"
#include "mqtt/exception.h"
#include "mqtt/message.h"
#include "mqtt/callback.h"
#include "mqtt/thread_queue.h"
#include "mqtt/iasync_client.h"
#include <vector>
#include <list>
#include <memory>
#include <tuple>
#include <functional>
#include <stdexcept>
namespace mqtt {
// OBSOLETE: The legacy constants that lacked the "PAHO_MQTTPP_" prefix
// clashed with #define's from other libraries and will be removed at the
// next major version upgrade.
#if defined(PAHO_MQTTPP_VERSIONS)
/** The version number for the client library. */
const uint32_t PAHO_MQTTPP_VERSION = 0x01040000;
/** The version string for the client library */
const string PAHO_MQTTPP_VERSION_STR("Paho MQTT C++ (mqttpp) v. 1.4.0");
/** Copyright notice for the client library */
const string PAHO_MQTTPP_COPYRIGHT("Copyright (c) 2013-2024 Frank Pagliughi");
#else
/** The version number for the client library. */
const uint32_t VERSION = 0x01040000;
/** The version string for the client library */
const string VERSION_STR("Paho MQTT C++ (mqttpp) v. 1.4.0");
/** Copyright notice for the client library */
const string COPYRIGHT("Copyright (c) 2013-2024 Frank Pagliughi");
#endif
/////////////////////////////////////////////////////////////////////////////
/**
* Client for talking to an MQTT server using non-blocking
* methods that allow an operation to run in the background.
*
* The location of the server is specified as a URI string with the
* following schemas supported to specify the type and security used for the
* connection:
* @li @em "mqtt://" - A standard (insecure) connection over TCP. (Also,
* "tcp://")
* @li @em "mqtts://" - A secure connection using SSL/TLS sockets. (Also
* "ssl://")
* @li @em "ws://" - A standard (insecure) WebSocket connection.
* @li @em "wss:// - A secure websocket connection using SSL/TLS.
*
* The secure connection types assume that the library was built with
* OpenSSL support, otherwise requesting a secure connection will result in
* an error.
*
* The communication methods of this class - `connect()`, `publish()`,
* `subscribe()`, etc. - are all asynchronous. They create the request for
* the server, but return immediately, before a response is received back
* from the server.
*
* These methods return a `Token` to the caller which is akin to a C++
* std::future. The caller can keep the Token, then use it later to block
* until the asynchronous operation is complete and retrieve the result of
* the operation, including any response from the server.
*
* Alternately, the application can choose to set callbacks to be fired when
* each operation completes. This can be used to create an event-driven
* architecture, but is more complex in that it forces the user to avoid any
* blocking operations and manually handle thread synchronization (since
* the callbacks run in a separate thread managed by the library).
*/
class async_client : public virtual iasync_client
{
public:
/** Smart/shared pointer for an object of this class */
using ptr_t = std::shared_ptr<async_client>;
/** Type for a thread-safe queue to consume messages synchronously */
using consumer_queue_type = std::unique_ptr<thread_queue<const_message_ptr>>;
/** Handler type for registering an individual message callback */
using message_handler = std::function<void(const_message_ptr)>;
/** Handler type for when a connection is made or lost */
using connection_handler = std::function<void(const string& cause)>;
/** Handler type for when a disconnect packet is received */
using disconnected_handler = std::function<void(const properties&, ReasonCode)>;
/** Handler for updating connection data before an auto-reconnect. */
using update_connection_handler = std::function<bool(connect_data&)>;
private:
/** Lock guard type for this class */
using guard = std::unique_lock<std::mutex>;
/** Unique lock type for this class */
using unique_lock = std::unique_lock<std::mutex>;
/** Object monitor mutex */
mutable std::mutex lock_;
/** The underlying C-lib client. */
MQTTAsync cli_;
/** The server URI string. */
string serverURI_;
/** The client ID string that we provided to the server. */
string clientId_;
/** The MQTT protocol version we're connected at */
int mqttVersion_;
/** A user persistence wrapper (if any) */
std::unique_ptr<MQTTClient_persistence> persist_;
/** Callback supplied by the user (if any) */
callback* userCallback_;
/** Connection handler */
connection_handler connHandler_;
/** Connection lost handler */
connection_handler connLostHandler_;
/** Disconnected handler */
disconnected_handler disconnectedHandler_;
/** Update connect data/options */
update_connection_handler updateConnectionHandler_;
/** Message handler */
message_handler msgHandler_;
/** Cached options from the last connect */
connect_options connOpts_;
/** Copy of connect token (for re-connects) */
token_ptr connTok_;
/** A list of tokens that are in play */
std::list<token_ptr> pendingTokens_;
/** A list of delivery tokens that are in play */
std::list<delivery_token_ptr> pendingDeliveryTokens_;
/** A queue of messages for consumer API */
consumer_queue_type que_;
/** Callbacks from the C library */
static void on_connected(void* context, char* cause);
static void on_connection_lost(void *context, char *cause);
static void on_disconnected(void* context, MQTTProperties* cprops,
MQTTReasonCodes reasonCode);
static int on_message_arrived(void* context, char* topicName, int topicLen,
MQTTAsync_message* msg);
static void on_delivery_complete(void* context, MQTTAsync_token tok);
static int on_update_connection(void* context, MQTTAsync_connectData* cdata);
/** Manage internal list of active tokens */
friend class token;
virtual void add_token(token_ptr tok);
virtual void add_token(delivery_token_ptr tok);
virtual void remove_token(token* tok) override;
virtual void remove_token(token_ptr tok) { remove_token(tok.get()); }
void remove_token(delivery_token_ptr tok) { remove_token(tok.get()); }
/** Non-copyable */
async_client() =delete;
async_client(const async_client&) =delete;
async_client& operator=(const async_client&) =delete;
/** Checks a function return code and throws on error. */
static void check_ret(int rc) {
if (rc != MQTTASYNC_SUCCESS)
throw exception(rc);
}
public:
/**
* Create an async_client that can be used to communicate with an MQTT
* server.
* This uses file-based persistence in the specified directory.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param persistDir The directory to use for persistence data
* @throw exception if an argument is invalid
*/
async_client(const string& serverURI, const string& clientId,
const string& persistDir);
/**
* Create an async_client that can be used to communicate with an MQTT
* server.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param persistence The user persistence structure. If this is null,
* then no persistence is used.
* @throw exception if an argument is invalid
*/
async_client(const string& serverURI, const string& clientId,
iclient_persistence* persistence=nullptr);
/**
* Create an async_client that can be used to communicate with an MQTT
* server, which allows for off-line message buffering.
* This uses file-based persistence in the specified directory.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
* @param persistDir The directory to use for persistence data
* @throw exception if an argument is invalid
*/
async_client(const string& serverURI, const string& clientId,
int maxBufferedMessages, const string& persistDir);
/**
* Create an async_client that can be used to communicate with an MQTT
* server, which allows for off-line message buffering.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
* @param persistence The user persistence structure. If this is null,
* then no persistence is used.
* @throw exception if an argument is invalid
*/
async_client(const string& serverURI, const string& clientId,
int maxBufferedMessages,
iclient_persistence* persistence=nullptr);
/**
* Create an async_client that can be used to communicate with an MQTT
* server, which allows for off-line message buffering.
* This uses file-based persistence in the specified directory.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param opts The create options
* @param persistDir The directory to use for persistence data
* @throw exception if an argument is invalid
*/
async_client(const string& serverURI, const string& clientId,
const create_options& opts, const string& persistDir);
/**
* Create an async_client that can be used to communicate with an MQTT
* server, which allows for off-line message buffering.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param opts The create options
* @param persistence The user persistence structure. If this is null,
* then no persistence is used.
* @throw exception if an argument is invalid
*/
async_client(const string& serverURI, const string& clientId,
const create_options& opts,
iclient_persistence* persistence=nullptr);
/**
* Destructor
*/
~async_client() override;
/**
* Sets a callback listener to use for events that happen
* asynchronously.
* @param cb callback receiver which will be invoked for certain
* asynchronous events
*/
void set_callback(callback& cb) override;
/**
* Stops callbacks.
* This is not normally called by the application. It should be used
* cautiously as it may cause the application to lose messages.
*/
void disable_callbacks() override;
/**
* Callback for when a connection is made.
* @param cb Callback functor for when the connection is made.
*/
void set_connected_handler(connection_handler cb) /*override*/;
/**
* Callback for when a connection is lost.
* @param cb Callback functor for when the connection is lost.
*/
void set_connection_lost_handler(connection_handler cb) /*override*/;
/**
* Callback for when a disconnect packet is received from the server.
* @param cb Callback for when the disconnect packet is received.
*/
void set_disconnected_handler(disconnected_handler cb) /*override*/;
/**
* Sets the callback for when a message arrives from the broker.
* Note that the application can only have one message handler which can
* be installed individually using this method, or installled as a
* listener object.
* @param cb The callback functor to register with the library.
*/
void set_message_callback(message_handler cb) /*override*/;
/**
* Sets a callback to allow the application to update the connection
* data on automatic reconnects.
* @param cb The callback functor to register with the library.
*/
void set_update_connection_handler(update_connection_handler cb);
/**
* Connects to an MQTT server using the default options.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
token_ptr connect() override;
/**
* Connects to an MQTT server using the provided connect options.
* @param options a set of connection parameters that override the
* defaults.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
token_ptr connect(connect_options options) override;
/**
* Connects to an MQTT server using the specified options.
* @param options a set of connection parameters that override the
* defaults.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when the connect
* completes.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
token_ptr connect(connect_options options, void* userContext,
iaction_listener& cb) override;
/**
*
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when the connect
* completes.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
token_ptr connect(void* userContext, iaction_listener& cb) override {
return connect(connect_options{}, userContext, cb);
}
/**
* Reconnects the client using options from the previous connect.
* The client must have previously called connect() for this to work.
* @return token used to track the progress of the reconnect.
*/
token_ptr reconnect() override;
/**
* Disconnects from the server.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
token_ptr disconnect() override { return disconnect(disconnect_options()); }
/**
* Disconnects from the server.
* @param opts Options for disconnecting.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
token_ptr disconnect(disconnect_options opts) override;
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @return Token used to track and wait for disconnect to complete. The
* token will be passed to the callback methods if a callback is
* set.
* @throw exception for problems encountered while disconnecting
*/
token_ptr disconnect(int timeout) override {
return disconnect(disconnect_options(timeout));
}
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @return Token used to track and wait for disconnect to complete. The
* token will be passed to the callback methods if a callback is
* set.
* @throw exception for problems encountered while disconnecting
*/
template <class Rep, class Period>
token_ptr disconnect(const std::chrono::duration<Rep, Period>& timeout) {
// TODO: check range
return disconnect((int) to_milliseconds_count(timeout));
}
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when the disconnect
* completes.
* @return token_ptr Token used to track and wait for disconnect to
* complete. The token will be passed to the callback methods if
* a callback is set.
* @throw exception for problems encountered while disconnecting
*/
token_ptr disconnect(int timeout, void* userContext,
iaction_listener& cb) override;
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when the disconnect
* completes.
* @return token_ptr Token used to track and wait for disconnect to
* complete. The token will be passed to the callback methods if
* a callback is set.
* @throw exception for problems encountered while disconnecting
*/
template <class Rep, class Period>
token_ptr disconnect(const std::chrono::duration<Rep, Period>& timeout,
void* userContext, iaction_listener& cb) {
// TODO: check range
return disconnect((int) to_milliseconds_count(timeout), userContext, cb);
}
/**
* Disconnects from the server.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when the disconnect
* completes.
* @return token_ptr Token used to track and wait for disconnect to
* complete. The token will be passed to the callback methods if
* a callback is set.
* @throw exception for problems encountered while disconnecting
*/
token_ptr disconnect(void* userContext, iaction_listener& cb) override {
return disconnect(0L, userContext, cb);
}
/**
* Returns the delivery token for the specified message ID.
* @return delivery_token
*/
delivery_token_ptr get_pending_delivery_token(int msgID) const override;
/**
* Returns the delivery tokens for any outstanding publish operations.
* @return delivery_token[]
*/
std::vector<delivery_token_ptr> get_pending_delivery_tokens() const override;
/**
* Returns the client ID used by this client.
* @return The client ID used by this client.
*/
string get_client_id() const override { return clientId_; }
/**
* Returns the address of the server used by this client.
* @return The server's address, as a URI String.
*/
string get_server_uri() const override { return serverURI_; }
/**
* Gets the MQTT version used by the client.
* @return The MQTT version used by the client
* @li MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if
* that fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
* @li MQTTVERSION_5 (5) = only try version 5
*/
int mqtt_version() const noexcept { return mqttVersion_; }
/**
* Gets a copy of the connect options that were last used in a request
* to connect to the broker.
* @returns The last connect options that were used.
*/
connect_options get_connect_options() const {
guard g(lock_);
return connOpts_;
}
/**
* Determines if this client is currently connected to the server.
* @return true if connected, false otherwise.
*/
bool is_connected() const override { return to_bool(MQTTAsync_isConnected(cli_)); }
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
delivery_token_ptr publish(string_ref topic, const void* payload, size_t n,
int qos, bool retained) override;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
delivery_token_ptr publish(string_ref topic, const void* payload, size_t n) override {
return publish(std::move(topic), payload, n,
message::DFLT_QOS, message::DFLT_RETAINED);
}
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
delivery_token_ptr publish(string_ref topic, binary_ref payload,
int qos, bool retained) override;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
delivery_token_ptr publish(string_ref topic, binary_ref payload) override {
return publish(std::move(topic), std::move(payload),
message::DFLT_QOS, message::DFLT_RETAINED);
}
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
delivery_token_ptr publish(string_ref topic,
const void* payload, size_t n,
int qos, bool retained,
void* userContext, iaction_listener& cb) override;
/**
* Publishes a message to a topic on the server Takes an Message
* message and delivers it to the server at the requested quality of
* service.
* @param msg the message to deliver to the server
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
delivery_token_ptr publish(const_message_ptr msg) override;
/**
* Publishes a message to a topic on the server.
* @param msg the message to deliver to the server
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback optional listener that will be notified when message
* delivery has completed to the requested quality of
* service
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
delivery_token_ptr publish(const_message_ptr msg,
void* userContext, iaction_listener& cb) override;
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter the topic to subscribe to, which can include
* wildcards.
* @param qos The quality of service for the subscription
* @param opts The MQTT v5 subscribe options for the topic
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr subscribe(const string& topicFilter, int qos,
const subscribe_options& opts=subscribe_options(),
const properties& props=properties()) override;
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter the topic to subscribe to, which can include
* wildcards.
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when subscribe has completed
* @param opts The MQTT v5 subscribe options for the topic
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr subscribe(const string& topicFilter, int qos,
void* userContext, iaction_listener& cb,
const subscribe_options& opts=subscribe_options(),
const properties& props=properties()) override;
/**
* Subscribe to multiple topics, each of which may include wildcards.
* @param topicFilters
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param opts The MQTT v5 subscribe options (one for each topic)
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr subscribe(const_string_collection_ptr topicFilters,
const qos_collection& qos,
const std::vector<subscribe_options>& opts=std::vector<subscribe_options>(),
const properties& props=properties()) override;
/**
* Subscribes to multiple topics, each of which may include wildcards.
* @param topicFilters
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when subscribe has completed
* @param opts The MQTT v5 subscribe options (one for each topic)
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr subscribe(const_string_collection_ptr topicFilters,
const qos_collection& qos,
void* userContext, iaction_listener& cb,
const std::vector<subscribe_options>& opts=std::vector<subscribe_options>(),
const properties& props=properties()) override;
/**
* Requests the server unsubscribe the client from a topic.
* @param topicFilter the topic to unsubscribe from. It must match a
* topicFilter specified on an earlier subscribe.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr unsubscribe(const string& topicFilter,
const properties& props=properties()) override;
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters one or more topics to unsubscribe from. Each
* topicFilter must match one specified on an
* earlier subscribe.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr unsubscribe(const_string_collection_ptr topicFilters,
const properties& props=properties()) override;
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when unsubscribe has
* completed
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr unsubscribe(const_string_collection_ptr topicFilters,
void* userContext, iaction_listener& cb,
const properties& props=properties()) override;
/**
* Requests the server unsubscribe the client from a topics.
* @param topicFilter the topic to unsubscribe from. It must match a
* topicFilter specified on an earlier subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when unsubscribe has
* completed
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
token_ptr unsubscribe(const string& topicFilter,
void* userContext, iaction_listener& cb,
const properties& props=properties()) override;
/**
* Start consuming messages.
* This initializes the client to receive messages through a queue that
* can be read synchronously.
*/
void start_consuming() override;
/**
* Stop consuming messages.
* This shuts down the internal callback and discards any unread
* messages.
*/
void stop_consuming() override;
/**
* Read the next message from the queue.
* This blocks until a new message arrives.
* @return The message and topic.
*/
const_message_ptr consume_message() override { return que_->get(); }
/**
* Try to read the next message from the queue without blocking.
* @param msg Pointer to the value to receive the message
* @return @em true is a message was read, @em false if no message was
* available.
*/
bool try_consume_message(const_message_ptr* msg) override {
return que_->try_get(msg);
}
/**
* Waits a limited time for a message to arrive.
* @param msg Pointer to the value to receive the message
* @param relTime The maximum amount of time to wait for a message.
* @return @em true if a message was read, @em false if a timeout
* occurred.
*/
template <typename Rep, class Period>
bool try_consume_message_for(const_message_ptr* msg,
const std::chrono::duration<Rep, Period>& relTime) {
return que_->try_get_for(msg, relTime);
}
/**
* Waits a limited time for a message to arrive.
* @param relTime The maximum amount of time to wait for a message.
* @return A shared pointer to the message that was received. It will be
* empty on timeout.
*/
template <typename Rep, class Period>
const_message_ptr try_consume_message_for(const std::chrono::duration<Rep, Period>& relTime) {
const_message_ptr msg;
que_->try_get_for(&msg, relTime);
return msg;
}
/**
* Waits until a specific time for a message to appear.
* @param msg Pointer to the value to receive the message
* @param absTime The time point to wait until, before timing out.
* @return @em true if a message was read, @em false if a timeout
* occurred.
*/
template <class Clock, class Duration>
bool try_consume_message_until(const_message_ptr* msg,
const std::chrono::time_point<Clock,Duration>& absTime) {
return que_->try_get_until(msg, absTime);
}
/**
* Waits until a specific time for a message to appear.
* @param absTime The time point to wait until, before timing out.
* @return The message, if read, an empty pointer if not.
*/
template <class Clock, class Duration>
const_message_ptr try_consume_message_until(const std::chrono::time_point<Clock,Duration>& absTime) {
const_message_ptr msg;
que_->try_get_until(&msg, absTime);
return msg;
}
};
/** Smart/shared pointer to an asynchronous MQTT client object */
using async_client_ptr = async_client::ptr_t;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_async_client_h

View File

@@ -0,0 +1,305 @@
/////////////////////////////////////////////////////////////////////////////
/// @file buffer_ref.h
/// Buffer reference type for the Paho MQTT C++ library.
/// @date April 18, 2017
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2017-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_buffer_ref_h
#define __mqtt_buffer_ref_h
#include "mqtt/types.h"
#include <iostream>
#include <cstring>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* A reference object for holding immutable data buffers, with cheap copy
* semantics and lifetime management.
*
* Each object of this class contains a reference-counted pointer to an
* immutable data buffer. Objects can be copied freely and easily, even
* across threads, since all instances promise not to modify the contents
* of the buffer.
*
* The buffer is immutable but the reference itself acts like a normal
* variable. It can be reassigned to point to a different buffer.
*
* If no value has been assigned to a reference, then it is in a default
* "null" state. It is not safe to call any member functions on a null
* reference, other than to check if the object is null or empty.
* @verbatim
* string_ref sr;
* if (!sr)
* cout << "null reference" << endl;
* else
* cout.write(sr.data(), sr.size());
* @endverbatim
*/
template <typename T>
class buffer_ref
{
public:
/**
* The underlying type for the buffer.
* Normally byte-wide data (char or uint8_t) for Paho.
*/
using value_type = T;
/**
* The type for the buffer.
* We use basic_string for compatibility with string data.
*/
using blob = std::basic_string<value_type>;
/**
* The pointer we use.
* Note that it is a pointer to a _const_ blob.
*/
using pointer_type = std::shared_ptr<const blob>;
private:
/** Our data is a shared pointer to a const buffer */
pointer_type data_;
public:
/**
* Default constructor creates a null reference.
*/
buffer_ref() =default;
/**
* Copy constructor only copies a shared pointer.
* @param buf Another buffer reference.
*/
buffer_ref(const buffer_ref& buf) =default;
/**
* Move constructor only moves a shared pointer.
* @param buf Another buffer reference.
*/
buffer_ref(buffer_ref&& buf) =default;
/**
* Creates a reference to a new buffer by copying data.
* @param b A string from which to create a new buffer.
*/
buffer_ref(const blob& b) : data_{std::make_shared<blob>(b)} {}
/**
* Creates a reference to a new buffer by moving a string into the
* buffer.
* @param b A string from which to create a new buffer.
*/
buffer_ref(blob&& b) : data_{std::make_shared<blob>(std::move(b))} {}
/**
* Creates a reference to an existing buffer by copying the shared
* pointer.
* Note that it is up to the caller to insure that there are no mutable
* references to the buffer.
* @param p A shared pointer to a string.
*/
buffer_ref(const pointer_type& p) : data_(p) {}
/**
* Creates a reference to an existing buffer by moving the shared
* pointer.
* Note that it is up to the caller to insure that there are no mutable
* references to the buffer.
* @param p A shared pointer to a string.
*/
buffer_ref(pointer_type&& p) : data_(std::move(p)) {}
/**
* Creates a reference to a new buffer containing a copy of the data.
* @param buf The memory to copy
* @param n The number of bytes to copy.
*/
buffer_ref(const value_type* buf, size_t n) : data_{std::make_shared<blob>(buf,n)} {}
/**
* Creates a reference to a new buffer containing a copy of the
* NUL-terminated char array.
* @param buf A NUL-terminated char array (C string).
*/
buffer_ref(const char* buf) : buffer_ref(reinterpret_cast<const value_type*>(buf),
std::strlen(buf)) {
static_assert(sizeof(char) == sizeof(T), "can only use C arr with char or byte buffers");
}
/**
* Copy the reference to the buffer.
* @param rhs Another buffer
* @return A reference to this object
*/
buffer_ref& operator=(const buffer_ref& rhs) =default;
/**
* Move a reference to a buffer.
* @param rhs The other reference to move.
* @return A reference to this object.
*/
buffer_ref& operator=(buffer_ref&& rhs) =default;
/**
* Copy a string into this object, creating a new buffer.
* Modifies the reference for this object, pointing it to a
* newly-created buffer. Other references to the old object remain
* unchanges, so this follows copy-on-write semantics.
* @param b A new blob/string to copy.
* @return A reference to this object.
*/
buffer_ref& operator=(const blob& b) {
data_.reset(new blob(b));
return *this;
}
/**
* Move a string into this object, creating a new buffer.
* Modifies the reference for this object, pointing it to a
* newly-created buffer. Other references to the old object remain
* unchanges, so this follows copy-on-write semantics.
* @param b A new blob/string to move.
* @return A reference to this object.
*/
buffer_ref& operator=(blob&& b) {
data_.reset(new blob(std::move(b)));
return *this;
}
/**
* Copy a NUL-terminated C char array into a new buffer
* @param cstr A NUL-terminated C string.
* @return A reference to this object
*/
buffer_ref& operator=(const char* cstr) {
static_assert(sizeof(char) == sizeof(T), "can only use C arr with char or byte buffers");
data_.reset(new blob(reinterpret_cast<const value_type*>(cstr), strlen(cstr)));
return *this;
}
/**
* Copy another type of buffer reference to this one.
* This can copy a buffer of different types, provided that the size of
* the data elements are the same. This is typically used to convert
* from char to byte, where the data is the same, but the interpretation
* is different. Note that this copies the underlying buffer.
* @param rhs A reference to a different type of buffer.
* @return A reference to this object.
*/
template <typename OT>
buffer_ref& operator=(const buffer_ref<OT>& rhs) {
static_assert(sizeof(OT) == sizeof(T), "Can only assign buffers if values the same size");
data_.reset(new blob(reinterpret_cast<const value_type*>(rhs.data()), rhs.size()));
return *this;
}
/**
* Clears the reference to nil.
*/
void reset() { data_.reset(); }
/**
* Determines if the reference is valid.
* If the reference is invalid then it is not safe to call @em any
* member functions other than @ref is_null() and @ref empty()
* @return @em true if referring to a valid buffer, @em false if the
* reference (pointer) is null.
*/
explicit operator bool() const { return bool(data_); }
/**
* Determines if the reference is invalid.
* If the reference is invalid then it is not safe to call @em any
* member functions other than @ref is_null() and @ref empty()
* @return @em true if the reference is null, @em false if it is
* referring to a valid buffer,
*/
bool is_null() const { return !data_; }
/**
* Determines if the buffer is empty.
* @return @em true if the buffer is empty or the reference is null,
* @em false if the buffer contains data.
*/
bool empty() const { return !data_ || data_->empty(); }
/**
* Gets a const pointer to the data buffer.
* @return A pointer to the data buffer.
*/
const value_type* data() const { return data_->data(); }
/**
* Gets the size of the data buffer.
* @return The size of the data buffer.
*/
size_t size() const { return data_->size(); }
/**
* Gets the size of the data buffer.
* @return The size of the data buffer.
*/
size_t length() const { return data_->length(); }
/**
* Gets the data buffer as a string.
* @return The data buffer as a string.
*/
const blob& str() const { return *data_; }
/**
* Gets the data buffer as a string.
* @return The data buffer as a string.
*/
const blob& to_string() const { return str(); }
/**
* Gets the data buffer as NUL-terminated C string.
* Note that the reference must be set to call this function.
* @return The data buffer as a string.
*/
const char* c_str() const { return data_->c_str(); }
/**
* Gets a shared pointer to the (const) data buffer.
* @return A shared pointer to the (const) data buffer.
*/
const pointer_type& ptr() const { return data_; }
/**
* Gets elemental access to the data buffer (read only)
* @param i The index into the buffer.
* @return The value at the specified index.
*/
const value_type& operator[](size_t i) const { return (*data_)[i]; }
};
/**
* Stream inserter for a buffer reference.
* This does a binary write of the data in the buffer.
* @param os The output stream.
* @param buf The buffer reference to write.
* @return A reference to the output stream.
*/
template <typename T>
std::ostream& operator<<(std::ostream& os, const buffer_ref<T>& buf) {
if (!buf.empty())
os.write(buf.data(), buf.size());
return os;
}
/////////////////////////////////////////////////////////////////////////////
/**
* A reference to a text buffer.
*/
using string_ref = buffer_ref<char>;
/**
* A reference to a binary buffer.
* Note that we're using char for the underlying data type to allow
* efficient moves to and from std::string's. Using a separate type
* indicates that the data may be arbitrary binary.
*/
using binary_ref = buffer_ref<char>;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_buffer_ref_h

View File

@@ -0,0 +1,135 @@
/////////////////////////////////////////////////////////////////////////////
/// @file buffer_view.h
/// Buffer reference type for the Paho MQTT C++ library.
/// @date April 18, 2017
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2017-2020 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_buffer_view_h
#define __mqtt_buffer_view_h
#include "mqtt/types.h"
#include <iostream>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* A reference to a contiguous sequence of items, with no ownership.
* This simply contains a pointer to a const array of items, and a size.
* This is a similar, but simplified version of the std::string_view
* class(es) in the C++17 standard.
*/
template <typename T>
class buffer_view
{
public:
/** The type of items to be held in the queue. */
using value_type = T;
/** The type used to specify number of items in the container. */
using size_type = size_t;
private:
/** Const pointer to the data array */
const value_type* data_;
/** The size of the array */
size_type sz_;
public:
/**
* Constructs a buffer view.
* @param data The data pointer
* @param n The number of items
*/
buffer_view(const value_type* data, size_type n)
: data_(data), sz_(n) {}
/**
* Constructs a buffer view to a whole string.
* This the starting pointer and length of the whole string.
* @param str The string.
*/
buffer_view(const std::basic_string<value_type>& str)
: data_(str.data()), sz_(str.size()) {}
/**
* Gets a pointer the first item in the view.
* @return A const pointer the first item in the view.
*/
const value_type* data() const { return data_; }
/**
* Gets the number of items in the view.
* @return The number of items in the view.
*/
size_type size() const { return sz_; }
/**
* Gets the number of items in the view.
* @return The number of items in the view.
*/
size_type length() const { return sz_; }
/**
* Access an item in the view.
* @param i The index of the item.
* @return A const reference to the requested item.
*/
const value_type& operator[](size_t i) const { return data_[i]; }
/**
* Gets a copy of the view as a string.
* @return A copy of the view as a string.
*/
std::basic_string<value_type> str() const {
return std::basic_string<value_type>(data_, sz_);
}
/**
* Gets a copy of the view as a string.
* @return A copy of the view as a string.
*/
string to_string() const {
static_assert(sizeof(char) == sizeof(T), "can only get string for char or byte buffers");
return string(reinterpret_cast<const char*>(data_), sz_);
}
};
/**
* Stream inserter for a buffer view.
* This does a binary write of the data in the buffer.
* @param os The output stream.
* @param buf The buffer reference to write.
* @return A reference to the output stream.
*/
template <typename T>
std::ostream& operator<<(std::ostream& os, const buffer_view<T>& buf) {
if (buf.size() > 0)
os.write(buf.data(), sizeof(T)*buf.size());
return os;
}
/** A buffer view for character string data. */
using string_view = buffer_view<char>;
/** A buffer view for binary data */
using binary_view = buffer_view<char>;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_buffer_view_h

View File

@@ -0,0 +1,87 @@
/////////////////////////////////////////////////////////////////////////////
/// @file callback.h
/// Declaration of MQTT callback class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2019 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_callback_h
#define __mqtt_callback_h
#include "MQTTAsync.h"
#include "mqtt/delivery_token.h"
#include "mqtt/types.h"
#include <vector>
#include <memory>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Provides a mechanism for tracking the completion of an asynchronous
* action.
*/
class callback
{
public:
/** Smart/shared pointer to an object of this type */
using ptr_t = std::shared_ptr<callback>;
/** Smart/shared pointer to a const object of this type */
using const_ptr_t = std::shared_ptr<const callback>;
/**
* Virtual destructor.
*/
virtual ~callback() {}
/**
* This method is called when the client is connected.
* Note that, in response to an initial connect(), the token from the
* connect call is also signaled with an on_success(). That occurs just
* before this is called.
*/
virtual void connected(const string& /*cause*/) {}
/**
* This method is called when the connection to the server is lost.
*/
virtual void connection_lost(const string& /*cause*/) {}
/**
* This method is called when a message arrives from the server.
*/
virtual void message_arrived(const_message_ptr /*msg*/) {}
/**
* Called when delivery for a message has been completed, and all
* acknowledgments have been received.
*/
virtual void delivery_complete(delivery_token_ptr /*tok*/) {}
};
/** Smart/shared pointer to a callback object */
using callback_ptr = callback::ptr_t;
/** Smart/shared pointer to a const callback object */
using const_callback_ptr = callback::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_callback_h

View File

@@ -0,0 +1,444 @@
/////////////////////////////////////////////////////////////////////////////
/// @file client.h
/// Declaration of MQTT client class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_client_h
#define __mqtt_client_h
#include "mqtt/async_client.h"
#include <future>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Lightweight client for talking to an MQTT server using methods that block
* until an operation completes.
*/
class client : private callback
{
/** An arbitrary, but relatively long timeout */
PAHO_MQTTPP_EXPORT static const std::chrono::seconds DFLT_TIMEOUT;
/** The default quality of service */
PAHO_MQTTPP_EXPORT static const int DFLT_QOS; // =1;
/** The actual client */
async_client cli_;
/** The longest time to wait for an operation to complete. */
std::chrono::milliseconds timeout_;
/** Callback supplied by the user (if any) */
callback* userCallback_;
/**
* Creates a shared pointer to an existing non-heap object.
* The shared pointer is given a no-op deleter, so it will not try to
* destroy the object when it goes out of scope. It is up to the caller
* to ensure that the object remains in memory for as long as there may
* be pointers to it.
* @param val A value which may live anywhere in memory (stack,
* file-scope, etc).
* @return A shared pointer to the object.
*/
template <typename T>
std::shared_ptr<T> ptr(const T& val) {
return std::shared_ptr<T>(const_cast<T*>(&val), [](T*){});
}
// User callbacks
// Most are launched in a separate thread, for convenience, except
// message_arrived, for performance.
void connected(const string& cause) override {
std::async(std::launch::async, &callback::connected, userCallback_, cause).wait();
}
void connection_lost(const string& cause) override {
std::async(std::launch::async,
&callback::connection_lost, userCallback_, cause).wait();
}
void message_arrived(const_message_ptr msg) override {
userCallback_->message_arrived(msg);
}
void delivery_complete(delivery_token_ptr tok) override {
std::async(std::launch::async, &callback::delivery_complete, userCallback_, tok).wait();
}
/** Non-copyable */
client() =delete;
client(const async_client&) =delete;
client& operator=(const async_client&) =delete;
public:
/** Smart pointer type for this object */
using ptr_t = std::shared_ptr<client>;
/** Type for a collection of QOS values */
using qos_collection = async_client::qos_collection;
/** Handler for updating connection data before an auto-reconnect. */
using update_connection_handler = async_client::update_connection_handler;
/**
* Create a client that can be used to communicate with an MQTT server.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param persistence The user persistence structure. If this is null,
* then no persistence is used.
*/
client(const string& serverURI, const string& clientId,
iclient_persistence* persistence=nullptr);
/**
* Create an async_client that can be used to communicate with an MQTT
* server.
* This uses file-based persistence in the specified directory.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param persistDir The directory to use for persistence data
*/
client(const string& serverURI, const string& clientId,
const string& persistDir);
/**
* Create a client that can be used to communicate with an MQTT server,
* which allows for off-line message buffering.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
* @param persistence The user persistence structure. If this is null,
* then no persistence is used.
*/
client(const string& serverURI, const string& clientId,
int maxBufferedMessages,
iclient_persistence* persistence=nullptr);
/**
* Create a client that can be used to communicate with an MQTT server,
* which allows for off-line message buffering.
* This uses file-based persistence in the specified directory.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
* @param persistDir The directory to use for persistence data
*/
client(const string& serverURI, const string& clientId,
int maxBufferedMessages, const string& persistDir);
/**
* Create an async_client that can be used to communicate with an MQTT
* server, which allows for off-line message buffering.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param opts The create options
* @param persistence The user persistence structure. If this is null,
* then no persistence is used.
*/
client(const string& serverURI, const string& clientId,
const create_options& opts,
iclient_persistence* persistence=nullptr);
/**
* Virtual destructor
*/
virtual ~client() {}
/**
* Connects to an MQTT server using the default options.
*/
virtual connect_response connect();
/**
* Connects to an MQTT server using the specified options.
* @param opts
*/
virtual connect_response connect(connect_options opts);
/**
* Reconnects the client using options from the previous connect.
* The client must have previously called connect() for this to work.
*/
virtual connect_response reconnect();
/**
* Disconnects from the server.
*/
virtual void disconnect();
/**
* Disconnects from the server.
* @param timeoutMS the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
*/
virtual void disconnect(int timeoutMS);
/**
* Disconnects from the server.
* @param to the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
*/
template <class Rep, class Period>
void disconnect(const std::chrono::duration<Rep, Period>& to) {
disconnect((int) to_milliseconds_count(to));
}
/**
* Gets the client ID used by this client.
* @return The client ID used by this client.
*/
virtual string get_client_id() const { return cli_.get_client_id(); }
/**
* Gets the address of the server used by this client.
* @return The address of the server used by this client, as a URI.
*/
virtual string get_server_uri() const { return cli_.get_server_uri(); }
/**
* Return the maximum time to wait for an action to complete.
* @return int
*/
virtual std::chrono::milliseconds get_timeout() const { return timeout_; }
/**
* Gets a copy of the connect options that were last used in a request
* to connect to the broker.
* @returns The last connect options that were used.
*/
connect_options get_connect_options() const {
return cli_.get_connect_options();
}
/**
* Get a topic object which can be used to publish messages on this
* client.
* @param top The topic name
* @param qos The Quality of Service for the topic
* @param retained Whether the published messages set the retain flag.
* @return A topic attached to this client.
*/
virtual topic get_topic(const string& top, int qos=message::DFLT_QOS,
bool retained=message::DFLT_RETAINED) {
return topic(cli_, top, qos, retained);
}
/**
* Determines if this client is currently connected to the server.
* @return @em true if the client is currently connected, @em false if
* not.
*/
virtual bool is_connected() const { return cli_.is_connected(); }
/**
* Sets a callback to allow the application to update the connection
* data on automatic reconnects.
* @param cb The callback functor to register with the library.
*/
void set_update_connection_handler(update_connection_handler cb) {
cli_.set_update_connection_handler(cb);
}
/**
* Publishes a message to a topic on the server and return once it is
* delivered.
* @param top The topic to publish
* @param payload The data to publish
* @param n The size in bytes of the data
* @param qos The QoS for message delivery
* @param retained Whether the broker should retain the message
*/
virtual void publish(string_ref top, const void* payload, size_t n,
int qos, bool retained) {
if (!cli_.publish(std::move(top), payload, n, qos, retained)->wait_for(timeout_))
throw timeout_error();
}
/**
* Publishes a message to a topic on the server and return once it is
* delivered.
* @param top The topic to publish
* @param payload The data to publish
* @param n The size in bytes of the data
*/
virtual void publish(string_ref top, const void* payload, size_t n) {
if (!cli_.publish(std::move(top), payload, n)->wait_for(timeout_))
throw timeout_error();
}
/**
* Publishes a message to a topic on the server.
* @param msg The message
*/
virtual void publish(const_message_ptr msg) {
if (!cli_.publish(msg)->wait_for(timeout_))
throw timeout_error();
}
/**
* Publishes a message to a topic on the server.
* This version will not timeout since that could leave the library with
* a reference to memory that could disappear while the library is still
* using it.
* @param msg The message
*/
virtual void publish(const message& msg) {
cli_.publish(ptr(msg))->wait();
}
/**
* Sets the callback listener to use for events that happen
* asynchronously.
* @param cb The callback functions
*/
virtual void set_callback(callback& cb);
/**
* Set the maximum time to wait for an action to complete.
* @param timeoutMS The timeout in milliseconds
*/
virtual void set_timeout(int timeoutMS) {
timeout_ = std::chrono::milliseconds(timeoutMS);
}
/**
* Set the maximum time to wait for an action to complete.
* @param to The timeout as a std::chrono duration.
*/
template <class Rep, class Period>
void set_timeout(const std::chrono::duration<Rep, Period>& to) {
timeout_ = to_milliseconds(to);
}
/**
* Subscribe to a topic, which may include wildcards using a QoS of 1.
* @param topicFilter
* @param props The MQTT v5 properties.
* @param opts The MQTT v5 subscribe options for the topic
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(const string& topicFilter,
const subscribe_options& opts=subscribe_options(),
const properties& props=properties());
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter A single topic to subscribe
* @param qos The QoS of the subscription
* @param opts The MQTT v5 subscribe options for the topic
* @param props The MQTT v5 properties.
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(const string& topicFilter, int qos,
const subscribe_options& opts=subscribe_options(),
const properties& props=properties());
/**
* Subscribes to a one or more topics, which may include wildcards using
* a QoS of 1.
* @param topicFilters A set of topics to subscribe
* @param opts The MQTT v5 subscribe options (one for each topic)
* @param props The MQTT v5 properties.
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(const string_collection& topicFilters,
const std::vector<subscribe_options>& opts=std::vector<subscribe_options>(),
const properties& props=properties());
/**
* Subscribes to multiple topics, each of which may include wildcards.
* @param topicFilters A collection of topics to subscribe
* @param qos A collection of QoS for each topic
* @param opts The MQTT v5 subscribe options (one for each topic)
* @param props The MQTT v5 properties.
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(const string_collection& topicFilters,
const qos_collection& qos,
const std::vector<subscribe_options>& opts=std::vector<subscribe_options>(),
const properties& props=properties());
/**
* Requests the server unsubscribe the client from a topic.
* @param topicFilter A single topic to unsubscribe.
* @param props The MQTT v5 properties.
* @return The "unsubscribe" response from the server.
*/
virtual unsubscribe_response unsubscribe(const string& topicFilter,
const properties& props=properties());
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters A collection of topics to unsubscribe.
* @param props The MQTT v5 properties.
* @return The "unsubscribe" response from the server.
*/
virtual unsubscribe_response unsubscribe(const string_collection& topicFilters,
const properties& props=properties());
/**
* Start consuming messages.
* This initializes the client to receive messages through a queue that
* can be read synchronously.
*/
virtual void start_consuming() { cli_.start_consuming(); }
/**
* Stop consuming messages.
* This shuts down the internal callback and discards any unread
* messages.
*/
virtual void stop_consuming() { cli_.stop_consuming(); }
/**
* Read the next message from the queue.
* This blocks until a new message arrives.
* @return The message and topic.
*/
virtual const_message_ptr consume_message() { return cli_.consume_message(); }
/**
* Try to read the next message from the queue without blocking.
* @param msg Pointer to the value to receive the message
* @return @em true is a message was read, @em false if no message was
* available.
*/
virtual bool try_consume_message(const_message_ptr* msg) {
return cli_.try_consume_message(msg);
}
/**
* Waits a limited time for a message to arrive.
* @param msg Pointer to the value to receive the message
* @param relTime The maximum amount of time to wait for a message.
* @return @em true if a message was read, @em false if a timeout
* occurred.
*/
template <typename Rep, class Period>
bool try_consume_message_for(const_message_ptr* msg,
const std::chrono::duration<Rep, Period>& relTime) {
return cli_.try_consume_message_for(msg, relTime);
}
/**
* Waits until a specific time for a message to occur.
* @param msg Pointer to the value to receive the message
* @param absTime The time point to wait until, before timing out.
* @return @em true if a message was read, @em false if a timeout
* occurred.
*/
template <class Clock, class Duration>
bool try_consume_message_until(const_message_ptr* msg,
const std::chrono::time_point<Clock,Duration>& absTime) {
return cli_.try_consume_message_until(msg, absTime);
}
};
/** Smart/shared pointer to an MQTT synchronous client object */
using client_ptr = client::ptr_t;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_client_h

View File

@@ -0,0 +1,968 @@
/////////////////////////////////////////////////////////////////////////////
/// @file connect_options.h
/// Declaration of MQTT connect_options class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2020 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_connect_options_h
#define __mqtt_connect_options_h
#include "MQTTAsync.h"
#include "mqtt/types.h"
#include "mqtt/message.h"
#include "mqtt/topic.h"
#include "mqtt/token.h"
#include "mqtt/string_collection.h"
#include "mqtt/will_options.h"
#include "mqtt/ssl_options.h"
#include "mqtt/platform.h"
#include <vector>
#include <map>
#include <chrono>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Holds the set of options that control how the client connects to a
* server.
*/
class connect_options
{
/** The default C struct for non-WebSocket connections */
PAHO_MQTTPP_EXPORT static const MQTTAsync_connectOptions DFLT_C_STRUCT;
/** The default C struct for non-Websocket MQTT v5 connections */
PAHO_MQTTPP_EXPORT static const MQTTAsync_connectOptions DFLT_C_STRUCT5;
/** The default C struct for WebSocket connections */
PAHO_MQTTPP_EXPORT static const MQTTAsync_connectOptions DFLT_C_STRUCT_WS;
/** The default C struct for Websocket MQTT v5 connections */
PAHO_MQTTPP_EXPORT static const MQTTAsync_connectOptions DFLT_C_STRUCT5_WS;
/** The underlying C connection options */
MQTTAsync_connectOptions opts_;
/** The LWT options */
will_options will_;
/** The SSL options */
ssl_options ssl_;
/** The user name to use for the connection. */
string_ref userName_;
/** The password to use for the connection. */
binary_ref password_;
/** Shared token pointer for context, if any */
token_ptr tok_;
/** Collection of server URIs, if any */
const_string_collection_ptr serverURIs_;
/** The connect properties */
properties props_;
/** HTTP Headers */
name_value_collection httpHeaders_;
/** HTTP proxy for websockets */
string httpProxy_;
/** Secure HTTPS proxy for websockets */
string httpsProxy_;
/** The client has special access */
friend class async_client;
/**
* Gets a pointer to the C-language NUL-terminated strings for the
* struct.
* @note In the connect options, by default, the Paho C treats
* nullptr char arrays as unset values, so we keep that semantic and
* only set those char arrays if the string is non-empty.
* @param str The C++ string object.
* @return Pointer to a NUL terminated string. This is only valid until
* the next time the string is updated.
*/
const char* c_str(const string_ref& sr) {
return sr.empty() ? nullptr : sr.c_str();
}
const char* c_str(const string& s) {
return s.empty() ? nullptr : s.c_str();
}
/**
* Updates the underlying C structure to match our strings.
*/
void update_c_struct();
/**
* Creates the options from a C option struct.
* @param copts The C options struct.
*/
connect_options(const MQTTAsync_connectOptions& copts) : opts_(copts) {}
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<connect_options>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const connect_options>;
/**
* Constructs a new object using the default values.
*
* @param ver The MQTT protocol version.
*/
explicit connect_options(int ver=MQTTVERSION_DEFAULT);
/**
* Constructs a new object using the specified user name and password.
* @param userName The name of the user for connecting to the server
* @param password The password for connecting to the server
* @param ver The MQTT protocol version.
*/
connect_options(
string_ref userName, binary_ref password,
int ver=MQTTVERSION_DEFAULT
);
/**
* Copy constructor.
* @param opt Another object to copy.
*/
connect_options(const connect_options& opt);
/**
* Move constructor.
* @param opt Another object to move into this new one.
*/
connect_options(connect_options&& opt);
/**
* Creates default options for an MQTT v3.x connection.
* @return Default options for an MQTT v3.x connection.
*/
static connect_options v3();
/**
* Creates default options for an MQTT v5 connection.
* @return Default options for an MQTT v5 connection.
*/
static connect_options v5();
/**
* Creates default options for an MQTT v3.x connection using WebSockets.
*
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return Default options for an MQTT v3.x connection using websockets.
*/
static connect_options ws() {
return connect_options(DFLT_C_STRUCT_WS);
}
/**
* Creates default options for an MQTT v5 connection using WebSockets.
*
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return Default options for an MQTT v5 connection using websockets.
*/
static connect_options v5_ws() {
return connect_options(DFLT_C_STRUCT5_WS);
}
/**
* Copy assignment.
* @param opt Another object to copy.
*/
connect_options& operator=(const connect_options& opt);
/**
* Move assignment.
* @param opt Another object to move into this new one.
*/
connect_options& operator=(connect_options&& opt);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_connectOptions& c_struct() const { return opts_; }
#endif
/**
* Gets the "keep alive" interval.
* @return The keep alive interval in seconds.
*/
std::chrono::seconds get_keep_alive_interval() const {
return std::chrono::seconds(opts_.keepAliveInterval);
}
/**
* Gets the connection timeout.
* This is the amount of time the underlying library will wait for a
* timeout before failing.
* @return The connect timeout in seconds.
*/
std::chrono::seconds get_connect_timeout() const {
return std::chrono::seconds(opts_.connectTimeout);
}
/**
* Gets the user name to use for the connection.
* @return The user name to use for the connection.
*/
string get_user_name() const { return userName_ ? userName_.to_string() : string(); }
/**
* Gets the password to use for the connection.
* @return The password to use for the connection.
*/
binary_ref get_password() const { return password_; }
/**
* Gets the password to use for the connection.
* @return The password to use for the connection.
*/
string get_password_str() const {
return password_ ? password_.to_string() : string();
}
/**
* Gets the maximum number of messages that can be in-flight
* simultaneously.
* @return The maximum number of inflight messages.
*/
int get_max_inflight() const { return opts_.maxInflight; }
/**
* Gets the topic to be used for last will and testament (LWT).
* @return The topic to be used for last will and testament (LWT).
*/
string get_will_topic() const {
return will_.get_topic();
}
/**
* Gets the message to be sent as last will and testament (LWT).
* @return The message to be sent as last will and testament (LWT).
*/
const_message_ptr get_will_message() const {
return will_.get_message();
}
/**
* Get the LWT options to use for the connection.
* @return The LWT options to use for the connection.
*/
const will_options& get_will_options() const { return will_; }
/**
* Get the SSL options to use for the connection.
* @return The SSL options to use for the connection.
*/
const ssl_options& get_ssl_options() const { return ssl_; }
/**
* Sets the SSL for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and using a secure connection, "ssl://" or
* "wss://".
* @param ssl The SSL options.
*/
void set_ssl(const ssl_options& ssl);
/**
* Sets the SSL for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and using a secure connection, "ssl://" or
* "wss://".
* @param ssl The SSL options.
*/
void set_ssl(ssl_options&& ssl);
/**
* Returns whether the server should remember state for the client
* across reconnects. This only applies to MQTT v3.x connections.
* @return @em true if requesting a clean session, @em false if not.
*/
bool is_clean_session() const { return to_bool(opts_.cleansession); }
/**
* Returns whether the server should remember state for the client
* across reconnects. This only applies to MQTT v5 connections.
* @return @em true if requesting a clean start, @em false if not.
*/
bool is_clean_start() const { return to_bool(opts_.cleanstart); }
/**
* Gets the token used as the callback context.
* @return The delivery token used as the callback context.
*/
token_ptr get_token() const { return tok_; }
/**
* Gets the list of servers to which the client will connect.
* @return A collection of server URI's. Each entry should be of the
* form @em protocol://host:port where @em protocol must be tcp
* or @em ssl. For @em host, you can specify either an IP
* address or a domain name.
*/
const_string_collection_ptr get_servers() const { return serverURIs_; }
/**
* Gets the version of MQTT to be used on the connect.
* @return
* @li MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that
* fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
*/
int get_mqtt_version() const { return opts_.MQTTVersion; }
/**
* Determines if the options have been configured for automatic
* reconnect.
* @return @em true if configured for automatic reconnect, @em false if
* not.
*/
bool get_automatic_reconnect() const { return to_bool(opts_.automaticReconnect); }
/**
* Gets the minimum retry interval for automatic reconnect.
* @return The minimum retry interval for automatic reconnect, in
* seconds.
*/
std::chrono::seconds get_min_retry_interval() const {
return std::chrono::seconds(opts_.minRetryInterval);
}
/**
* Gets the maximum retry interval for automatic reconnect.
* @return The maximum retry interval for automatic reconnect, in
* seconds.
*/
std::chrono::seconds get_max_retry_interval() const {
return std::chrono::seconds(opts_.maxRetryInterval);
}
/**
* Sets whether the server should remember state for the client across
* reconnects. (MQTT v3.x only)
*
* This will only take effect if the version is _already_ set to v3.x
* (not v5).
*
* @param clean @em true if the server should NOT remember state for the
* client across reconnects, @em false otherwise.
*/
void set_clean_session(bool cleanSession);
/**
* Sets whether the server should remember state for the client across
* reconnects. (MQTT v5 only)
*
* If a persistent session is desired (turning this off), then the app
* should also set the `Session Expiry Interval` property, and add that
* to the connect options.
*
* This will only take effect if the MQTT version is set to v5
*
* @param clean @em true if the server should NOT remember state for the
* client across reconnects, @em false otherwise.
*/
void set_clean_start(bool cleanStart);
/**
* Sets the "keep alive" interval.
* This is the maximum time that should pass without communications
* between client and server. If no messages pass in this time, the
* client will ping the broker.
* @param keepAliveInterval The keep alive interval in seconds.
*/
void set_keep_alive_interval(int keepAliveInterval) {
opts_.keepAliveInterval = keepAliveInterval;
}
/**
* Sets the "keep alive" interval with a chrono duration.
* This is the maximum time that should pass without communications
* between client and server. If no messages pass in this time, the
* client will ping the broker.
* @param interval The keep alive interval.
*/
template <class Rep, class Period>
void set_keep_alive_interval(const std::chrono::duration<Rep, Period>& interval) {
// TODO: Check range
set_keep_alive_interval((int) to_seconds_count(interval));
}
/**
* Sets the connect timeout in seconds.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param timeout The connect timeout in seconds.
*/
void set_connect_timeout(int timeout) {
opts_.connectTimeout = timeout;
}
/**
* Sets the connect timeout with a chrono duration.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param timeout The connect timeout in seconds.
*/
template <class Rep, class Period>
void set_connect_timeout(const std::chrono::duration<Rep, Period>& timeout) {
// TODO: check range
set_connect_timeout((int) to_seconds_count(timeout));
}
/**
* Sets the user name to use for the connection.
* @param userName The user name for connecting to the MQTT broker.
*/
void set_user_name(string_ref userName);
/**
* Sets the password to use for the connection.
* @param password The password for connecting to the MQTT broker.
*/
void set_password(binary_ref password);
/**
* Sets the maximum number of messages that can be in-flight
* simultaneously.
* @param n The maximum number of inflight messages.
*/
void set_max_inflight(int n) { opts_.maxInflight = n; }
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
void set_will(const will_options& will);
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
void set_will(will_options&& will);
/**
* Sets the "Last Will and Testament" (LWT) as a message
* @param msg The LWT message
*/
void set_will_message(const message& msg) {
set_will(will_options(msg));
}
/**
* Sets the "Last Will and Testament" (LWT) as a message
* @param msg Pointer to a LWT message
*/
void set_will_message(const_message_ptr msg) {
if (msg) set_will(will_options(*msg));
}
/**
* Sets the callback context to a delivery token.
* @param tok The delivery token to be used as the callback context.
*/
void set_token(const token_ptr& tok);
/**
* Sets the list of servers to which the client will connect.
* @param serverURIs A pointer to a collection of server URI's. Each
* entry should be of the form @em
* protocol://host:port where @em protocol must be
* @em tcp or @em ssl. For @em host, you can specify
* either an IP address or a domain name.
*/
void set_servers(const_string_collection_ptr serverURIs);
/**
* Sets the version of MQTT to be used on the connect.
*
* This will also set other connect options to legal values dependent on
* the selected version.
*
* @param mqttVersion The MQTT version to use for the connection:
* @li MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if
* that fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
* @li MQTTVERSION_5 (5) = only try version 5
*
* @deprecated It is preferable to create the options for the desired
* version rather than using this function to change the version after
* some parameters have already been set. If you do use this function,
* call it before setting any other version-specific options. @sa
* connect_options::v5()
*/
void set_mqtt_version(int mqttVersion);
/**
* Enable or disable automatic reconnects.
* The retry intervals are not affected.
* @param on Whether to turn reconnects on or off
*/
void set_automatic_reconnect(bool on) {
opts_.automaticReconnect = to_int(on);
}
/**
* Enable or disable automatic reconnects.
* @param minRetryInterval Minimum retry interval in seconds. Doubled
* on each failed retry.
* @param maxRetryInterval Maximum retry interval in seconds. The
* doubling stops here on failed retries.
*/
void set_automatic_reconnect(int minRetryInterval, int maxRetryInterval);
/**
* Enable or disable automatic reconnects.
* @param minRetryInterval Minimum retry interval. Doubled on each
* failed retry.
* @param maxRetryInterval Maximum retry interval. The doubling stops
* here on failed retries.
*/
template <class Rep1, class Period1, class Rep2, class Period2>
void set_automatic_reconnect(const std::chrono::duration<Rep1, Period1>& minRetryInterval,
const std::chrono::duration<Rep2, Period2>& maxRetryInterval) {
set_automatic_reconnect((int) to_seconds_count(minRetryInterval),
(int) to_seconds_count(maxRetryInterval));
}
/**
* Gets the connect properties.
* @return A const reference to the properties for the connect.
*/
const properties& get_properties() const { return props_; }
/**
* Gets a mutable reference to the connect properties.
* @return A reference to the properties for the connect.
*/
properties& get_properties() { return props_; }
/**
* Sets the properties for the connect.
* @param props The properties to place into the message.
*/
void set_properties(const properties& props);
/**
* Moves the properties for the connect.
* @param props The properties to move into the connect object.
*/
void set_properties(properties&& props);
/**
* Gets the HTTP headers
* @return A const reference to the HTTP headers name/value collection.
*/
const name_value_collection& get_http_headers() const {
return httpHeaders_;
}
/**
* Sets the HTTP headers for the connection.
* @param httpHeaders The header nam/value collection.
*/
void set_http_headers(const name_value_collection& httpHeaders) {
httpHeaders_ = httpHeaders;
opts_.httpHeaders = httpHeaders_.empty() ? nullptr : httpHeaders_.c_arr();
}
/**
* Sets the HTTP headers for the connection.
* @param httpHeaders The header nam/value collection.
*/
void set_http_headers(name_value_collection&& httpHeaders) {
httpHeaders_ = std::move(httpHeaders);
opts_.httpHeaders = httpHeaders_.empty() ? nullptr : httpHeaders_.c_arr();
}
/**
* Gets the HTTP proxy setting.
* @return The HTTP proxy setting. An empty string means no proxy.
*/
string get_http_proxy() const { return httpProxy_; }
/**
* Sets the HTTP proxy setting.
* @param httpProxy The HTTP proxy setting. An empty string means no
* proxy.
*/
void set_http_proxy(const string& httpProxy);
/**
* Gets the secure HTTPS proxy setting.
* @return The HTTPS proxy setting. An empty string means no proxy.
*/
string get_https_proxy() const { return httpsProxy_; }
/**
* Sets the secure HTTPS proxy setting.
* @param httpsProxy The HTTPS proxy setting. An empty string means no
* proxy.
*/
void set_https_proxy(const string& httpsProxy);
};
/** Smart/shared pointer to a connection options object. */
using connect_options_ptr = connect_options::ptr_t;
/////////////////////////////////////////////////////////////////////////////
/**
* The connect options that can be updated before an automatic reconnect.
*/
class connect_data
{
/** The default C struct */
PAHO_MQTTPP_EXPORT static const MQTTAsync_connectData DFLT_C_STRUCT;
/** The underlying C connect data */
MQTTAsync_connectData data_;
/** The user name to use for the connection. */
string_ref userName_;
/** The password to use for the connection. (Optional) */
binary_ref password_;
/** The client has special access */
friend class async_client;
/**
* Updates the underlying C structure to match our strings.
*/
void update_c_struct();
/**
* Create data from a C struct
* This is a deep copy of the data from the C struct.
* @param cdata The C connect data.
*/
connect_data(const MQTTAsync_connectData& cdata);
public:
/**
* Creates an empty set of connection data.
*/
connect_data();
/**
* Creates connection data with a user name, but no password.
* @param userName The user name for reconnecting to the MQTT broker.
*/
explicit connect_data(string_ref userName);
/**
* Creates connection data with a user name and password.
* @param userName The user name for reconnecting to the MQTT broker.
* @param password The password for connecting to the MQTT broker.
*/
connect_data(string_ref userName, binary_ref password);
/**
* Copy constructor
* @param other Another data struct to copy into this one.
*/
connect_data(const connect_data& other);
/**
* Copy the connection data.
* @param rhs Another data struct to copy into this one.
* @return A reference to this data
*/
connect_data& operator=(const connect_data& rhs);
/**
* Gets the user name to use for the connection.
* @return The user name to use for the connection.
*/
string get_user_name() const { return userName_ ? userName_.to_string() : string(); }
/**
* Gets the password to use for the connection.
* @return The password to use for the connection.
*/
binary_ref get_password() const { return password_; }
/**
* Sets the user name to use for the connection.
* @param userName The user name for connecting to the MQTT broker.
*/
void set_user_name(string_ref userName);
/**
* Sets the password to use for the connection.
* @param password The password for connecting to the MQTT broker.
*/
void set_password(binary_ref password);
};
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build connect options.
*/
class connect_options_builder
{
connect_options opts_;
public:
/** This class */
using self = connect_options_builder;
/**
* Default constructor.
*
* @param ver The MQTT version for the connection. Defaults to the most
* recent v3 supported by the server.
*/
explicit connect_options_builder(int ver=MQTTVERSION_DEFAULT) : opts_(ver) {}
/**
* Copy constructor from an existing set of options.
*/
explicit connect_options_builder(const connect_options& opts) : opts_(opts) {}
/**
* Move constructor from an existing set of options.
*/
explicit connect_options_builder(const connect_options&& opts) : opts_(std::move(opts)) {}
/**
* Creates the default options builder for an MQTT v3.x connection.
* @return An options builder for an MQTT v3.x connection.
*/
static connect_options_builder v3() {
return connect_options_builder{ connect_options::v3() };
}
/**
* Creates the default options builder for an MQTT v5 connection.
* @return An options builder for an MQTT v5 connection.
*/
static connect_options_builder v5() {
return connect_options_builder{ connect_options::v5() };
}
/**
* Creates the default options builder for an MQTT v3.x connection using
* WebSockets.
*
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return An options builder for an MQTT v3.x connection using
* websockets.
*/
static connect_options_builder ws() {
return connect_options_builder{ connect_options::ws() };
}
/**
* Creates the default options for an MQTT v5 connection using
* WebSockets
* .
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return An options builder for an MQTT v5 connection using
* websockets.
*/
static connect_options_builder v5_ws() {
return connect_options_builder{ connect_options::v5_ws() };
}
/**
* Sets whether the server should remember state for the client across
* reconnects. (MQTT v3.x only)
* @param on @em true if the server should NOT remember state for the
* client across reconnects, @em false otherwise.
*/
auto clean_session(bool on=true) -> self& {
opts_.set_clean_session(on);
return *this;
}
/**
* Sets the "keep alive" interval with a chrono duration.
* This is the maximum time that should pass without communications
* between client and server. If no messages pass in this time, the
* client will ping the broker.
* @param interval The keep alive interval.
*/
template <class Rep, class Period>
auto keep_alive_interval(const std::chrono::duration<Rep, Period>& interval) -> self& {
opts_.set_keep_alive_interval(interval);
return *this;
}
/**
* Sets the connect timeout with a chrono duration.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param timeout The connect timeout in seconds.
*/
template <class Rep, class Period>
auto connect_timeout(const std::chrono::duration<Rep, Period>& timeout) -> self& {
opts_.set_connect_timeout(timeout);
return *this;
}
/**
* Sets the user name to use for the connection.
* @param userName
*/
auto user_name(string_ref userName) -> self& {
opts_.set_user_name(userName);
return *this;
}
/**
* Sets the password to use for the connection.
*/
auto password(binary_ref password) -> self& {
opts_.set_password(password);
return *this;
}
/**
* Sets the maximum number of messages that can be in-flight
* simultaneously.
* @param n The maximum number of inflight messages.
*/
auto max_inflight(int n) -> self& {
opts_.set_max_inflight(n);
return *this;
}
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
auto will(const will_options& will) -> self& {
opts_.set_will(will);
return *this;
}
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
auto will(will_options&& will) -> self& {
opts_.set_will(std::move(will));
return *this;
}
/**
* Sets the "Last Will and Testament" (LWT) as a message
* @param msg The LWT message
*/
auto will(const message& msg) -> self& {
opts_.set_will_message(msg);
return *this;
}
/**
* Sets the SSL options for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and connecting with a secure URI.
* @param ssl The SSL options.
*/
auto ssl(const ssl_options& ssl) -> self& {
opts_.set_ssl(ssl);
return *this;
}
/**
* Sets the SSL options for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and connecting with a secure URI.
* @param ssl The SSL options.
*/
auto ssl(ssl_options&& ssl) -> self& {
opts_.set_ssl(std::move(ssl));
return *this;
}
/**
* Sets the callback context to a delivery token.
* @param tok The delivery token to be used as the callback context.
*/
auto token(const token_ptr& tok) -> self& {
opts_.set_token(tok);
return *this;
}
/**
* Sets the list of servers to which the client will connect.
* @param serverURIs A pointer to a collection of server URI's. Each
* entry should be of the form @em
* protocol://host:port where @em protocol must be
* @em tcp or @em ssl. For @em host, you can specify
* either an IP address or a domain name.
*/
auto servers(const_string_collection_ptr serverURIs) -> self& {
opts_.set_servers(serverURIs);
return *this;
}
/**
* Sets the version of MQTT to be used on the connect.
*
* This will also set other connect options to legal values dependent on
* the selected version.
*
* @param ver The MQTT protocol version to use for the connection:
* @li MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if
* that fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
* @li MQTTVERSION_5 (5) = only try version 5
*
* @deprecated It is preferable to create the options builder for the
* desired version rather than using this function to change the
* version after some parameters have already been set. If you do use
* this function, call it before setting any other version-specific
* options. @sa connect_options_builder::v5()
*/
auto mqtt_version(int ver) -> self& {
opts_.set_mqtt_version(ver);
return *this;
}
/**
* Enable or disable automatic reconnects.
* The retry intervals are not affected.
* @param on Whether to turn reconnects on or off
*/
auto automatic_reconnect(bool on=true) -> self& {
opts_.set_automatic_reconnect(on);
return *this;
}
/**
* Enable or disable automatic reconnects.
* @param minRetryInterval Minimum retry interval. Doubled on each
* failed retry.
* @param maxRetryInterval Maximum retry interval. The doubling stops
* here on failed retries.
*/
template <class Rep1, class Period1, class Rep2, class Period2>
auto automatic_reconnect(const std::chrono::duration<Rep1, Period1>& minRetryInterval,
const std::chrono::duration<Rep2, Period2>& maxRetryInterval) -> self& {
opts_.set_automatic_reconnect(minRetryInterval, maxRetryInterval);
return *this;
}
/**
* Sets the 'clean start' flag for the connection. (MQTT v5 only)
* @param on @em true to set the 'clean start' flag for the connect,
* @em false otherwise.
*/
auto clean_start(bool on=true) -> self& {
opts_.set_clean_start(on);
return *this;
}
/**
* Sets the properties for the connect message.
* @param props The properties for the connect message.
*/
auto properties(const mqtt::properties& props) -> self& {
opts_.set_properties(props);
return *this;
}
/**
* Sets the properties for the connect message.
* @param props The properties for the connect message.
*/
auto properties(mqtt::properties&& props) -> self& {
opts_.set_properties(std::move(props));
return *this;
}
/**
* Sets the HTTP headers for the connection.
* @param headers The header nam/value collection.
*/
auto http_headers(const name_value_collection& headers) -> self& {
opts_.set_http_headers(headers);
return *this;
}
/**
* Sets the HTTP headers for the connection.
* @param headers The header nam/value collection.
*/
auto http_headers(name_value_collection&& headers) -> self& {
opts_.set_http_headers(std::move(headers));
return *this;
}
/**
* Sets the HTTP proxy setting.
* @param httpProxy The HTTP proxy setting. An empty string means no
* proxy.
*/
auto http_proxy(const string& httpProxy) -> self& {
opts_.set_http_proxy(httpProxy);
return *this;
}
/**
* Sets the secure HTTPS proxy setting.
* @param httpsProxy The HTTPS proxy setting. An empty string means no
* proxy.
*/
auto https_proxy(const string& httpsProxy) -> self& {
opts_.set_https_proxy(httpsProxy);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
connect_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_connect_options_h

View File

@@ -0,0 +1,271 @@
/////////////////////////////////////////////////////////////////////////////
/// @file create_options.h
/// Declaration of MQTT create_options class
/// @date Oct 17, 2020
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2020-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_create_options_h
#define __mqtt_create_options_h
#include "MQTTAsync.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Options for creating a client object.
*/
class create_options
{
/** The default C struct */
static const MQTTAsync_createOptions DFLT_C_STRUCT;
/** The underlying C options */
MQTTAsync_createOptions opts_;
/** The client and tests have special access */
friend class async_client;
friend class create_options_builder;
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<create_options>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const create_options>;
/**
* Default set of client create options.
*/
create_options();
/**
* Default create options for the specified version of MQTT.
* @param mqttVersion The MQTT version used to create the client.
*/
explicit create_options(int mqttVersion);
/**
* Default create options, but with off-line buffering enabled.
* @param mqttVersion The MQTT version used to create the client.
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
*/
create_options(int mqttVersion, int maxBufferedMessages);
/**
* Gets whether the client will accept message to publish while
* disconnected.
*/
bool get_send_while_disconnected() const {
return to_bool(opts_.sendWhileDisconnected);
}
/**
* Sets whether the client will accept message to publish while
* disconnected.
*
* @param on @em true to allow the application to publish messages while
* disconnected, @em false returns an error on publish if
* disconnected.
* @param anyTime If @em true, allows you to publish messages before the
* first successful connection.
*/
void set_send_while_disconnected(bool on, bool anyTime=false) {
opts_.sendWhileDisconnected = to_int(on);
opts_.allowDisconnectedSendAtAnyTime = to_int(anyTime);
}
/**
* Gets the maximum number of offline buffered messages.
* @return The maximum number of offline buffered messages.
*/
int get_max_buffered_messages() const {
return opts_.maxBufferedMessages;
}
/**
* Sets the maximum number of offline buffered messages.
* @param n The maximum number of offline buffered messages.
*/
void set_max_buffered_messages(int n) {
opts_.maxBufferedMessages = n;
}
/**
* Gets the MQTT version used to create the client.
* @return The MQTT version used to create the client.
*/
int mqtt_version() const { return opts_.MQTTVersion; }
/**
* Sets the MQTT version used to create the client.
* @param ver The MQTT version used to create the client.
*/
void set_mqtt_version(int ver) { opts_.MQTTVersion = ver; }
/**
* Whether the oldest messages are deleted when the output buffer is
* full.
*
* @return @em true if the oldest messages should be deleted when the
* output buffer is full, @em false if the new messages should
* be dropped when the buffer is full.
*/
bool get_delete_oldest_messages() const {
return to_bool(opts_.deleteOldestMessages);
}
/**
* Determines what to do when the maximum number of buffered messages is
* reached: delete the oldest messages rather than the newest
* @param on @em true When the output queue is full, delete the oldest
* message, @em false drop the newest message being added.
*/
void set_delete_oldest_messages(bool on) {
opts_.deleteOldestMessages = to_int(on);
}
/**
* Whether the messages will be restored from persistence or the store
* will be cleared.
* @return @em true if the messages will be restored from persistence,
* @em false if the persistence store will be cleared.
*/
bool get_restore_messages() const {
return to_bool(opts_.restoreMessages);
}
/**
* Determine whether to restore messages from persistence or clear the
* persistence store.
* @param on @em true to restore messages from persistence, @em false to
* clear the persistence store.
*/
void set_restore_messages(bool on) {
opts_.restoreMessages = to_int(on);
}
/**
* Whether to persist QoS 0 messages.
*
* @return @em true if QoS 0 messages are persisted, @em false if not.
*/
bool get_persist_qos0() const {
return to_bool(opts_.persistQoS0);
}
/**
* Determine whether to persist QoS 0 messages.
*
* @param on @em true if QoS 0 messages are persisted, @em false if not.
*/
void set_persist_qos0(bool on) {
opts_.persistQoS0 = to_int(on);
}
};
/** Smart/shared pointer to a connection options object. */
using create_options_ptr = create_options::ptr_t;
/////////////////////////////////////////////////////////////////////////////
/**
* Builder class to generate the create options.
*/
class create_options_builder
{
/** The underlying options */
create_options opts_;
public:
/** This class */
using self = create_options_builder;
/**
* Default constructor.
*/
create_options_builder() {}
/**
*
* Sets whether the client will accept message to publish while
* disconnected.
*
* @param on @em true to allow the application to publish messages while
* disconnected, @em false returns an error on publish if
* disconnected.
* @param anyTime If @em true, allows you to publish messages before the
* first successful connection.
* @return A reference to this object.
*/
auto send_while_disconnected(bool on=true, bool anyTime=false) -> self& {
opts_.opts_.sendWhileDisconnected = to_int(on);
opts_.opts_.allowDisconnectedSendAtAnyTime = to_int(anyTime);
return *this;
}
/**
* Sets the maximum number of offline buffered messages.
* @param n The maximum number of offline buffered messages.
* @return A reference to this object.
*/
auto max_buffered_messages(int n) -> self& {
opts_.opts_.maxBufferedMessages = n;
return *this;
}
/**
* Sets the MQTT version used to create the client.
* @param ver The MQTT version used to create the client.
*/
auto mqtt_version(int ver) -> self& {
opts_.opts_.MQTTVersion = ver;
return *this;
}
/**
* Determines what to do when the maximum number of buffered messages is
* reached: delete the oldest messages rather than the newest.
* @param on @em true When the output queue is full, delete the oldest
* message, @em false drop the newest message being added.
* @return A reference to this object.
*/
auto delete_oldest_messages(bool on=true) -> self& {
opts_.opts_.deleteOldestMessages = to_int(on);
return *this;
}
/**
* Determines whether to restore persisted messages or clear the
* persistence store. (Defaults true)
*
* @param on @em true to restore persisted messages, @em false to clear
* the persistence store.
* @return A reference to this object.
*/
auto restore_messages(bool on=true) -> self& {
opts_.opts_.restoreMessages = to_int(on);
return *this;
}
/**
* Whether to persist QoS 0 messages. (Defaults true)
*
* @param on @em true persist QoS 0 messages, @em false, don't.
* @return A reference to this object
*/
auto persist_qos0(bool on=true) -> self& {
opts_.opts_.persistQoS0 = to_int(on);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
create_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_create_options_h

View File

@@ -0,0 +1,135 @@
/////////////////////////////////////////////////////////////////////////////
/// @file delivery_token.h
/// Declaration of MQTT delivery_token class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_delivery_token_h
#define __mqtt_delivery_token_h
#include "MQTTAsync.h"
#include "mqtt/token.h"
#include "mqtt/message.h"
#include <memory>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Provides a mechanism to track the delivery progress of a message.
* Used to track the the delivery progress of a message when a publish is
* executed in a non-blocking manner (run in the background) action.
*/
class delivery_token : public token
{
/** The message being tracked. */
const_message_ptr msg_;
/** Client has special access. */
friend class async_client;
/**
* Sets the message to which this token corresponds.
* @param msg
*/
void set_message(const_message_ptr msg) { msg_ = msg; }
public:
/** Smart/shared pointer to an object of this class */
using ptr_t = std::shared_ptr<delivery_token>;
/** Smart/shared pointer to a const object of this class */
using const_ptr_t = std::shared_ptr<delivery_token>;
/** Weak pointer to an object of this class */
using weak_ptr_t = std::weak_ptr<delivery_token>;
/**
* Creates an empty delivery token connected to a particular client.
* @param cli The asynchronous client object.
*/
delivery_token(iasync_client& cli) : token(token::Type::PUBLISH, cli) {}
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message being tracked.
*/
delivery_token(iasync_client& cli, const_message_ptr msg)
: token(token::Type::PUBLISH, cli, msg->get_topic()), msg_(std::move(msg)) {}
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message data.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback optional listener that will be notified when message
* delivery has completed to the requested quality of
* service
*/
delivery_token(iasync_client& cli, const_message_ptr msg,
void* userContext, iaction_listener& cb)
: token(token::Type::PUBLISH, cli, msg->get_topic(), userContext, cb), msg_(std::move(msg)) {}
/**
* Creates an empty delivery token connected to a particular client.
* @param cli The asynchronous client object.
*/
static ptr_t create(iasync_client& cli) {
return std::make_shared<delivery_token>(cli);
}
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message data.
*/
static ptr_t create(iasync_client& cli, const_message_ptr msg) {
return std::make_shared<delivery_token>(cli, msg);
}
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message data.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback optional listener that will be notified when message
* delivery has completed to the requested quality of
* service
*/
static ptr_t create(iasync_client& cli, const_message_ptr msg,
void* userContext, iaction_listener& cb) {
return std::make_shared<delivery_token>(cli, msg, userContext, cb);
}
/**
* Gets the message associated with this token.
* @return The message associated with this token.
*/
virtual const_message_ptr get_message() const { return msg_; }
};
/** Smart/shared pointer to a delivery_token */
using delivery_token_ptr = delivery_token::ptr_t;
/** Smart/shared pointer to a const delivery_token */
using const_delivery_token_ptr = delivery_token::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_delivery_token_h

View File

@@ -0,0 +1,281 @@
/////////////////////////////////////////////////////////////////////////////
/// @file disconnect_options.h
/// Implementation of the class 'disconnect_options'
/// @date 26-Aug-2016
/////////////////////////////////////////////////////////////////////////////
/****************************************************************************
* Copyright (c) 2016-2017 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
***************************************************************************/
#ifndef __mqtt_disconnect_options_h
#define __mqtt_disconnect_options_h
#include "MQTTAsync.h"
#include "mqtt/types.h"
#include "mqtt/token.h"
#include "mqtt/properties.h"
#include <chrono>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Options for disconnecting from an MQTT broker.
*/
class disconnect_options
{
/** The default C struct */
static const MQTTAsync_disconnectOptions DFLT_C_STRUCT;
/** The default C struct */
static const MQTTAsync_disconnectOptions DFLT_C_STRUCT5;
/** The underlying C disconnect options */
MQTTAsync_disconnectOptions opts_;
/** Shared token pointer for context, if any */
token_ptr tok_;
/** Disconnect message properties */
properties props_;
/** The client has special access */
friend class async_client;
/** The options builder has special access */
friend class disconnect_options_builder;
/**
* Updates the underlying C structure to match our cached data.
*/
void update_c_struct();
/** Construct options from a C struct */
disconnect_options(const MQTTAsync_disconnectOptions& copts) : opts_{copts} {}
public:
/**
* Create an empty delivery response object.
*/
disconnect_options();
/**
* Creates disconnect options tied to the specific token.
* @param timeout The timeout (in milliseconds).
*/
disconnect_options(int timeout) : disconnect_options() {
set_timeout(timeout);
}
/**
* Creates disconnect options tied to the specific token.
* @param to The timeout.
*/
template <class Rep, class Period>
disconnect_options(const std::chrono::duration<Rep, Period>& to)
: disconnect_options() {
set_timeout(to);
}
/**
* Copy constructor.
* @param opt Another object to copy.
*/
disconnect_options(const disconnect_options& opt);
/**
* Move constructor.
* @param opt Another object to move into this new one.
*/
disconnect_options(disconnect_options&& opt);
/**
* Creates default options for an MQTT v3.x connection.
* @return Default options for an MQTT v3.x connection.
*/
static disconnect_options v3();
/**
* Creates default options for an MQTT v5 connection.
* @return Default options for an MQTT v5 connection.
*/
static disconnect_options v5();
/**
* Copy assignment.
* @param opt Another object to copy.
*/
disconnect_options& operator=(const disconnect_options& opt);
/**
* Move assignment.
* @param opt Another object to move into this new one.
*/
disconnect_options& operator=(disconnect_options&& opt);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_disconnectOptions& c_struct() const { return opts_; }
#endif
/**
* Gets the timeout used for disconnecting.
* @return The timeout for disconnecting (in milliseconds).
*/
std::chrono::milliseconds get_timeout() const {
return std::chrono::milliseconds(opts_.timeout);
}
/**
* Sets the disconnect timeout, in milliseconds.
* This allows for any remaining in-flight messages to be delivered.
* @param timeout The disconnect timeout (in milliseconds).
*/
void set_timeout(int timeout) { opts_.timeout = timeout; }
/**
* Sets the disconnect timeout with a duration.
* This allows for any remaining in-flight messages to be delivered.
* @param to The disconnect connect timeout.
*/
template <class Rep, class Period>
void set_timeout(const std::chrono::duration<Rep, Period>& to) {
// TODO: check range
set_timeout((int) to_milliseconds_count(to));
}
/**
* Sets the callback context to a delivery token.
* @param tok The delivery token to be used as the callback context.
* @param mqttVersion The version of MQTT we're using for the
* connection.
*/
void set_token(const token_ptr& tok, int mqttVersion);
/**
* Gets the callback context to a delivery token.
* @return The delivery token to be used as the callback context.
*/
token_ptr get_token() const { return tok_; }
/**
* Gets the disconnect properties.
* @return A const reference to the properties for the disconnect.
*/
const properties& get_properties() const { return props_; }
/**
* Gets a mutable reference to the disconnect properties.
* @return A mutable reference to the properties for the disconnect.
*/
properties& get_properties() { return props_; }
/**
* Sets the properties for the connect.
* @param props The properties to place into the message.
*/
void set_properties(const properties& props) {
props_ = props;
opts_.properties = props_.c_struct();
}
/**
* Moves the properties for the connect.
* @param props The properties to move into the connect object.
*/
void set_properties(properties&& props) {
props_ = std::move(props);
opts_.properties = props_.c_struct();
}
/**
* Gets the reason code for the disconnect.
* @return The reason code for the disconnect.
*/
ReasonCode get_reason_code() const {
return ReasonCode(opts_.reasonCode);
}
/**
* Sets the reason code for the disconnect.
* @param code The reason code for the disconnect.
*/
void set_reason_code(ReasonCode code) {
opts_.reasonCode = MQTTReasonCodes(code);
}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build connect options.
*/
class disconnect_options_builder
{
/** The underlying options */
disconnect_options opts_;
/** Construct options builder from a C struct */
disconnect_options_builder(const MQTTAsync_disconnectOptions& copts) : opts_{copts} {}
public:
/** This class */
using self = disconnect_options_builder;
/**
* Default constructor.
*/
disconnect_options_builder() {}
/**
* Creates default options builder for an MQTT v3.x connection.
* @return Default options builder for an MQTT v3.x connection.
*/
static disconnect_options_builder v3();
/**
* Creates default options builder for an MQTT v5 connection.
* @return Default options builder for an MQTT v5 connection.
*/
static disconnect_options_builder v5();
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(mqtt::properties&& props) -> self& {
opts_.set_properties(std::move(props));
return *this;
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(const mqtt::properties& props) -> self& {
opts_.set_properties(props);
return *this;
}
/**
* Sets the disconnect connect timeout.
* This allows for any remaining in-flight messages to be delivered.
* @param to The disconnect timeout.
*/
template <class Rep, class Period>
auto timeout(const std::chrono::duration<Rep, Period>& to) -> self&{
opts_.set_timeout(to);
return *this;
}
/**
* Sets the reason code for the disconnect.
* @param code The reason code for the disconnect.
*/
auto reason_code(ReasonCode code) -> self& {
opts_.set_reason_code(code);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
disconnect_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
// end namespace 'mqtt'
}
#endif // __mqtt_disconnect_options_h

View File

@@ -0,0 +1,261 @@
/////////////////////////////////////////////////////////////////////////////
/// @file exception.h
/// Declaration of MQTT exception class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2019 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_exception_h
#define __mqtt_exception_h
#include "MQTTAsync.h"
#include "mqtt/types.h"
#include <iostream>
#include <vector>
#include <memory>
#include <exception>
#include <stdexcept>
namespace mqtt {
/** Bring std::bad_cast into the mqtt namespace */
using bad_cast = std::bad_cast;
/////////////////////////////////////////////////////////////////////////////
/**
* Base mqtt::exception.
* This wraps the error codes which originate from the underlying C library.
*/
class exception : public std::runtime_error
{
protected:
/** The error return code from the C library */
int rc_;
/** The reason code from the server */
ReasonCode reasonCode_;
/** The error message from the C library */
string msg_;
public:
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
*/
explicit exception(int rc)
: exception(rc, error_str(rc)) {}
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
* @param reasonCode The reason code from the server response.
*/
explicit exception(int rc, ReasonCode reasonCode)
: exception(rc, reasonCode, error_str(rc)) {}
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
* @param msg The text message for the error.
*/
exception(int rc, const string& msg)
: std::runtime_error(printable_error(rc, ReasonCode::SUCCESS, msg)),
rc_(rc), reasonCode_(ReasonCode::SUCCESS), msg_(msg) {}
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
* @param reasonCode The reason code from the server
* @param msg The text message for the error.
*/
exception(int rc, ReasonCode reasonCode, const string& msg)
: std::runtime_error(printable_error(rc, reasonCode, msg)),
rc_(rc), reasonCode_(reasonCode), msg_(msg) {}
/**
* Gets an error message from an error code.
* @param rc The error code from the C lib
* @return A string explanation of the error
*/
static string error_str(int rc) {
const char *msg = ::MQTTAsync_strerror(rc);
return msg ? string(msg) : string();
}
/**
* Gets a string describing the MQTT v5 reason code.
* @param reasonCode The MQTT v5 reason code.
* @return A string describing the reason code.
*/
static string reason_code_str(int reasonCode) {
if (reasonCode != MQTTPP_V3_CODE) {
auto msg = ::MQTTReasonCode_toString(MQTTReasonCodes(reasonCode));
if (msg) return string(msg);
}
return string();
}
/**
* Gets a detailed error message for an error code.
* @param rc The error code from the C lib
* @param reasonCode The MQTT v5 reason code
* @param msg An optional additional message. If none is provided, the
* error_str message is used.
* @return A string error message that includes the error code and an
* explanation message.
*/
static string printable_error(int rc, int reasonCode=ReasonCode::SUCCESS,
const string& msg=string()) {
string s = "MQTT error [" + std::to_string(rc) + "]";
if (!msg.empty())
s += string(": ") + msg;
if (reasonCode != MQTTPP_V3_CODE && reasonCode != ReasonCode::SUCCESS)
s += string(". Reason: ") + reason_code_str(reasonCode);
return s;
}
/**
* Returns the return code for this exception.
*/
int get_return_code() const { return rc_; }
/**
* Gets a string of the error code.
* @return A string of the error code.
*/
string get_error_str() const { return error_str(rc_); }
/**
* Returns the reason code for this exception.
* For MQTT v3 connections, this is actually the return code.
*/
int get_reason_code() const {
return reasonCode_ == MQTTPP_V3_CODE ? rc_ : reasonCode_;
}
/**
* Gets a string for the reason code.
* @return A string for the reason code.
*/
string get_reason_code_str() const {
return reason_code_str(reasonCode_);
}
/**
* Returns the error message for this exception.
*/
string get_message() const { return msg_; }
/**
* Gets a string representation of this exception.
* @return A string representation of this exception.
*/
string to_string() const { return string(what()); }
};
/**
* Stream inserter writes a fairly verbose message
* @param os The stream.
* @param exc The exception to write.
* @return A reference to the stream.
*/
inline std::ostream& operator<<(std::ostream& os, const exception& exc) {
os << exc.what();
return os;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Exception thrown when an expected server response is missing.
*/
class missing_response : public exception
{
public:
/**
* Create a missing response error.
* @param rsp A string for the type of response expected.
*/
missing_response(const string& rsp)
: exception(MQTTASYNC_FAILURE, "Missing "+rsp+" response") {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* A timeout exception, particularly from the synchronous client.
*/
class timeout_error : public exception
{
public:
/**
* Create a timeout error.
*/
timeout_error() : exception(MQTTASYNC_FAILURE, "Timeout") {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* This exception is thrown by the implementor of the persistence interface
* if there is a problem reading or writing persistent data.
*/
class persistence_exception : public exception
{
public:
/**
* Creates an MQTT persistence exception.
*/
persistence_exception() : exception(MQTTCLIENT_PERSISTENCE_ERROR) {}
/**
* Creates an MQTT persistence exception.
* @param code The error code from the C library.
*/
explicit persistence_exception(int code) : exception(code) {}
/**
* Creates an MQTT persistence exception.
* @param msg The text message for the error.
*/
explicit persistence_exception(const string& msg)
: exception(MQTTCLIENT_PERSISTENCE_ERROR, msg) {}
/**
* Creates an MQTT persistence exception.
* @param code The error code
* @param msg The text message for the error.
*/
persistence_exception(int code, const string& msg)
: exception(code, msg) {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Thrown when a client is not authorized to perform an operation, or if
* there is a problem with the security configuration.
*/
class security_exception : public exception
{
public:
/**
* Creates an MQTT security exception
* @param code The error code.
*/
explicit security_exception(int code) : exception(code) {}
/**
* Creates an MQTT security exception
* @param code The error code.
* @param msg The text message for the error.
*/
security_exception(int code, const string& msg) : exception(code, msg) {}
};
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_token_h

View File

@@ -0,0 +1,45 @@
/////////////////////////////////////////////////////////////////////////////
/// @file export.h
/// Library symbol export definitions, primarily for Windows MSVC DLL's
/// @date November 20, 2023
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
* Frank Pagliughi - MQTT v5 support
*******************************************************************************/
#ifndef __mqtt_export_h
#define __mqtt_export_h
#if defined(_WIN32) && defined(_MSC_VER)
#if defined(PAHO_MQTTPP_EXPORTS)
#define PAHO_MQTTPP_EXPORT __declspec(dllexport)
#elif defined(PAHO_MQTTPP_IMPORTS)
#define PAHO_MQTTPP_EXPORT __declspec(dllimport)
#else
#define PAHO_MQTTPP_EXPORT
#endif
#else
#if defined(PAHO_MQTTPP_EXPORTS)
#define PAHO_MQTTPP_EXPORT __attribute__ ((visibility ("default")))
#else
#define PAHO_MQTTPP_EXPORT
#endif
#endif
#endif // __mqtt_export_h

View File

@@ -0,0 +1,84 @@
/////////////////////////////////////////////////////////////////////////////
/// @file iaction_listener.h
/// Declaration of MQTT iaction_listener class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_iaction_listener_h
#define __mqtt_iaction_listener_h
#include "MQTTAsync.h"
#include "mqtt/types.h"
#include <vector>
namespace mqtt {
class token;
/////////////////////////////////////////////////////////////////////////////
/**
* Provides a mechanism for tracking the completion of an asynchronous
* action.
*
* A listener is registered on a token and that token is associated with
* an action like connect or publish. When used with tokens on the
* async_client the listener will be called back on the MQTT client's
* thread. The listener will be informed if the action succeeds or fails. It
* is important that the listener returns control quickly otherwise the
* operation of the MQTT client will be stalled.
*/
class iaction_listener
{
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<iaction_listener>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const iaction_listener>;
/**
* Virtual base destructor.
*/
virtual ~iaction_listener() {}
/**
* This method is invoked when an action fails.
* @param asyncActionToken
*/
virtual void on_failure(const token& asyncActionToken) =0;
/**
* This method is invoked when an action has completed successfully.
* @param asyncActionToken
*/
virtual void on_success(const token& asyncActionToken) =0;
};
/** Smart/shared pointer to an action listener */
using iaction_listener_ptr = iaction_listener::ptr_t;
/** Smart/shared pointer to a const action listener */
using const_iaction_listener_ptr = iaction_listener::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_iaction_listener_h

View File

@@ -0,0 +1,460 @@
/////////////////////////////////////////////////////////////////////////////
/// @file iasync_client.h
/// Implementation of the interface for the asynchronous clients,
/// 'iasync_client'
/// @date 25-Aug-2016
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_iasync_client_h
#define __mqtt_iasync_client_h
#include "mqtt/types.h"
#include "mqtt/token.h"
#include "mqtt/delivery_token.h"
#include "mqtt/iclient_persistence.h"
#include "mqtt/iaction_listener.h"
#include "mqtt/connect_options.h"
#include "mqtt/disconnect_options.h"
#include "mqtt/subscribe_options.h"
#include "mqtt/exception.h"
#include "mqtt/message.h"
#include "mqtt/callback.h"
#include <vector>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Enables an application to communicate with an MQTT server using
* non-blocking methods.
*
* It provides applications a simple programming interface to all features
* of the MQTT version 3.1 specification including:
*
* @li connect
* @li publish
* @li subscribe
* @li unsubscribe
* @li disconnect
*/
class iasync_client
{
friend class token;
virtual void remove_token(token* tok) =0;
public:
/** Type for a collection of QOS values */
using qos_collection = std::vector<int>;
/**
* Virtual destructor
*/
virtual ~iasync_client() {}
/**
* Connects to an MQTT server using the default options.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect() =0;
/**
* Connects to an MQTT server using the provided connect options.
* @param options a set of connection parameters that override the
* defaults.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect(connect_options options) =0;
/**
* Connects to an MQTT server using the specified options.
*
* @param options a set of connection parameters that override the
* defaults.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when the connect
* completes.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect(connect_options options, void* userContext,
iaction_listener& cb) =0;
/**
*
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when the connect completes.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect(void* userContext, iaction_listener& cb) =0;
/**
* Reconnects the client using options from the previous connect.
* The client must have previously called connect() for this to work.
* @return token used to track the progress of the reconnect.
*/
virtual token_ptr reconnect() =0;
/**
* Disconnects from the server.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect() =0;
/**
* Disconnects from the server.
* @param opts Options for disconnecting.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(disconnect_options opts) =0;
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(int timeout) =0;
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when the disconnect
* completes.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(int timeout, void* userContext, iaction_listener& cb) =0;
/**
* Disconnects from the server.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when the disconnect
* completes.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(void* userContext, iaction_listener& cb) =0;
/**
* Returns the delivery token for the specified message ID.
* @return delivery_token
*/
virtual delivery_token_ptr get_pending_delivery_token(int msgID) const =0;
/**
* Returns the delivery tokens for any outstanding publish operations.
* @return delivery_token[]
*/
virtual std::vector<delivery_token_ptr> get_pending_delivery_tokens() const =0;
/**
* Returns the client ID used by this client.
* @return string
*/
virtual string get_client_id() const =0;
/**
* Returns the address of the server used by this client.
*/
virtual string get_server_uri() const =0;
/**
* Determines if this client is currently connected to the server.
*/
virtual bool is_connected() const =0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(string_ref topic,
const void* payload, size_t n,
int qos, bool retained) =0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(string_ref topic,
const void* payload, size_t n) =0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(string_ref topic,
const void* payload, size_t n,
int qos, bool retained,
void* userContext, iaction_listener& cb) =0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(string_ref topic, binary_ref payload,
int qos, bool retained) =0;
/**
* Publishes a message to a topic on the server.
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(string_ref topic, binary_ref payload) =0;
/**
* Publishes a message to a topic on the server Takes an Message
* message and delivers it to the server at the requested quality of
* service.
* @param msg the message to deliver to the server
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(const_message_ptr msg) =0;
/**
* Publishes a message to a topic on the server.
* @param msg the message to deliver to the server
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb optional listener that will be notified when message
* delivery has completed to the requested quality of service
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(const_message_ptr msg,
void* userContext, iaction_listener& cb) =0;
/**
* Sets a callback listener to use for events that happen
* asynchronously.
* @param cb callback which will be invoked for certain asynchronous
* events
*/
virtual void set_callback(callback& cb) =0;
/**
* Stops the callbacks.
*/
virtual void disable_callbacks() =0;
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter the topic to subscribe to, which can include
* wildcards.
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param opts The options for the subscription.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(const string& topicFilter, int qos,
const subscribe_options& opts=subscribe_options(),
const properties& props=properties()) =0;
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter the topic to subscribe to, which can include
* wildcards.
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param callback listener that will be notified when subscribe has
* completed
* @param opts The options for the subscription.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(const string& topicFilter, int qos,
void* userContext, iaction_listener& callback,
const subscribe_options& opts=subscribe_options(),
const properties& props=properties()) =0;
/**
* Subscribe to multiple topics, each of which may include wildcards.
* Provides an optimized way to subscribe to multiple topics compared to
* subscribing to each one individually.
* @param topicFilters one or more topics to subscribe to, which can
* include wildcards
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param opts A collection of subscription options (one for each
* topic)
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(const_string_collection_ptr topicFilters,
const qos_collection& qos,
const std::vector<subscribe_options>& opts=std::vector<subscribe_options>(),
const properties& props=properties()) =0;
/**
* Subscribes to multiple topics, each of which may include wildcards.
* @param topicFilters one or more topics to subscribe to, which can
* include wildcards
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param callback listener that will be notified when subscribe has
* completed
* @param opts A collection of subscription options (one for each
* topic)
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(const_string_collection_ptr topicFilters,
const qos_collection& qos,
void* userContext, iaction_listener& callback,
const std::vector<subscribe_options>& opts=std::vector<subscribe_options>(),
const properties& props=properties()) =0;
/**
* Requests the server unsubscribe the client from a topic.
* @param topicFilter the topic to unsubscribe from. It must match a
* topicFilter specified on an earlier subscribe.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(const string& topicFilter,
const properties& props=properties()) =0;
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters one or more topics to unsubscribe from. Each
* topicFilter must match one specified on an
* earlier subscribe.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(const_string_collection_ptr topicFilters,
const properties& props=properties()) =0;
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters one or more topics to unsubscribe from. Each
* topicFilter must match one specified on an
* earlier subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when unsubscribe has
* completed
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(const_string_collection_ptr topicFilters,
void* userContext, iaction_listener& cb,
const properties& props=properties()) =0;
/**
* Requests the server unsubscribe the client from a topics.
* @param topicFilter the topic to unsubscribe from. It must match a
* topicFilter specified on an earlier subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when unsubscribe has
* completed.
* @param props The MQTT v5 properties.
* @return Token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(const string& topicFilter,
void* userContext, iaction_listener& cb,
const properties& props=properties()) =0;
/**
* Start consuming messages.
* This initializes the client to receive messages through a queue that
* can be read synchronously.
*/
virtual void start_consuming() =0;
/**
* Stop consuming messages.
* This shuts down the internal callback and discards any unread
* messages.
*/
virtual void stop_consuming() =0;
/**
* Read the next message from the queue.
* This blocks until a new message arrives.
* @return The message and topic.
*/
virtual const_message_ptr consume_message() =0;
/**
* Try to read the next message from the queue without blocking.
* @param msg Pointer to the value to receive the message
* @return @em true is a message was read, @em false if no message was
* available.
*/
virtual bool try_consume_message(const_message_ptr* msg) =0;
};
/////////////////////////////////////////////////////////////////////////////
// end namespace 'mqtt'
}
#endif // __mqtt_iasync_client_h

View File

@@ -0,0 +1,154 @@
/////////////////////////////////////////////////////////////////////////////
/// @file iclient_persistence.h
/// Declaration of MQTT iclient_persistence interface
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_iclient_persistence_h
#define __mqtt_iclient_persistence_h
#include "MQTTAsync.h"
#include "mqtt/types.h"
#include "mqtt/buffer_view.h"
#include "mqtt/string_collection.h"
#include <vector>
namespace mqtt {
/**
* Allocate memory for use with user persistence.
*
* @param n The number of bytes for the buffer.
* @return A pointer to the allocated memory
*/
inline void* persistence_malloc(size_t n) {
return MQTTAsync_malloc(n);
}
/**
* Frees memory allocated with @ref persistence_malloc().
* @param p Pointer to a buffer obtained by persistence_malloc.
*/
inline void persistence_free(void* p) {
MQTTAsync_free(p);
}
/////////////////////////////////////////////////////////////////////////////
/**
* Represents a persistent data store, used to store outbound and inbound
* messages while they are in flight, enabling delivery to the QoS
* specified. You can specify an implementation of this interface using
* client::client(string, string, iclient_persistence), which the
* client will use to persist QoS 1 and 2 messages.
*
* If the methods defined throw the MqttPersistenceException then the state
* of the data persisted should remain as prior to the method being called.
* For example, if put(string, persistable) throws an exception at any
* point then the data will be assumed to not be in the persistent store.
* Similarly if remove(string) throws an exception then the data will be
* assumed to still be held in the persistent store.
*
* It is up to the persistence interface to log any exceptions or error
* information which may be required when diagnosing a persistence failure.
*/
class iclient_persistence
{
friend class async_client;
friend class mock_persistence;
/** Callbacks from the C library */
static int persistence_open(void** handle, const char* clientID, const char* serverURI, void* context);
static int persistence_close(void* handle);
static int persistence_put(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
static int persistence_get(void* handle, char* key, char** buffer, int* buflen);
static int persistence_remove(void* handle, char* key);
static int persistence_keys(void* handle, char*** keys, int* nkeys);
static int persistence_clear(void* handle);
static int persistence_containskey(void* handle, char* key);
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<iclient_persistence>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const iclient_persistence>;
/**
* Virtual destructor.
*/
virtual ~iclient_persistence() {}
/**
* Initialize the persistent store.
* This uses the client ID and server name to create a unique location
* for the data store.
* @param clientId The identifier string for the client.
* @param serverURI The server to which the client is connected.
*/
virtual void open(const string& clientId, const string& serverURI) =0;
/**
* Close the persistent store that was previously opened.
*/
virtual void close() =0;
/**
* Clears persistence, so that it no longer contains any persisted data.
*/
virtual void clear() =0;
/**
* Returns whether or not data is persisted using the specified key.
* @param key The key to find
* @return @em true if the key exists, @em false if not.
*/
virtual bool contains_key(const string& key) =0;
/**
* Returns a collection of keys in this persistent data store.
* @return A collection of strings representing the keys in the store.
*/
virtual string_collection keys() const =0;
/**
* Puts the specified data into the persistent store.
* @param key The key.
* @param bufs The data to store
*/
virtual void put(const string& key, const std::vector<string_view>& bufs) =0;
/**
* Gets the specified data out of the persistent store.
* @param key The key
* @return A const view of the data associated with the key.
*/
virtual string get(const string& key) const =0;
/**
* Remove the data for the specified key.
* @param key The key
*/
virtual void remove(const string& key) =0;
};
/** Smart/shared pointer to a persistence client */
using iclient_persistence_ptr = iclient_persistence::ptr_t;
/** Smart/shared pointer to a persistence client */
using const_iclient_persistence_ptr = iclient_persistence::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_iclient_persistence_h

View File

@@ -0,0 +1,507 @@
/////////////////////////////////////////////////////////////////////////////
/// @file message.h
/// Declaration of MQTT message class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
* Frank Pagliughi - MQTT v5 support (properties)
*******************************************************************************/
#ifndef __mqtt_message_h
#define __mqtt_message_h
#include "MQTTAsync.h"
#include "mqtt/buffer_ref.h"
#include "mqtt/properties.h"
#include "mqtt/exception.h"
#include "mqtt/platform.h"
#include <memory>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* An MQTT message holds everything required for an MQTT PUBLISH message.
* This holds the binary message payload, topic string, and all the
* additional meta-data for an MQTT message.
*
* The topic and payload buffers are kept as references to const data, so
* they can be reassigned as needed, but the buffers can not be updated
* in-place. Normally they would be created externally then copied or moved
* into the message. The library to transport the messages never touches the
* payloads or topics.
*
* This also means that message objects are fairly cheap to copy, since they
* don't copy the payloads. They simply copy the reference to the buffers.
* It is safe to pass these buffer references across threads since all
* references promise not to update the contents of the buffer.
*/
class message
{
public:
/** The default QoS for a message */
PAHO_MQTTPP_EXPORT static const int DFLT_QOS; // =0
/** The default retained flag */
PAHO_MQTTPP_EXPORT static const bool DFLT_RETAINED; // =false
private:
/** Initializer for the C struct (from the C library) */
PAHO_MQTTPP_EXPORT static const MQTTAsync_message DFLT_C_STRUCT;
/** The underlying C message struct */
MQTTAsync_message msg_;
/** The topic that the message was (or should be) sent on. */
string_ref topic_;
/** The message payload - an arbitrary binary blob. */
binary_ref payload_;
/** The properties for the message */
properties props_;
/** The client has special access. */
friend class async_client;
/**
* Set the dup flag in the underlying message
* @param dup
*/
void set_duplicate(bool dup) { msg_.dup = to_int(dup); }
public:
/** Smart/shared pointer to this class. */
using ptr_t = std::shared_ptr<message>;
/** Smart/shared pointer to this class. */
using const_ptr_t = std::shared_ptr<const message>;
/**
* Constructs a message with an empty payload, and all other values set
* to defaults.
*/
message();
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
message(string_ref topic, const void* payload, size_t len,
int qos, bool retained,
const properties& props=properties());
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
*/
message(string_ref topic, const void* payload, size_t len)
: message(std::move(topic), payload, len, DFLT_QOS, DFLT_RETAINED) {}
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
message(string_ref topic, binary_ref payload, int qos, bool retained,
const properties& props=properties());
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
*/
message(string_ref topic, binary_ref payload)
: message(std::move(topic), std::move(payload), DFLT_QOS, DFLT_RETAINED) {}
/**
* Constructs a message as a copy of the message structure.
* @param topic The message topic
* @param cmsg A "C" MQTTAsync_message structure.
*/
message(string_ref topic, const MQTTAsync_message& cmsg);
/**
* Constructs a message as a copy of the other message.
* @param other The message to copy into this one.
*/
message(const message& other);
/**
* Moves the other message to this one.
* @param other The message to move into this one.
*/
message(message&& other);
/**
* Destroys a message and frees all associated resources.
*/
~message() {}
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
static ptr_t create(string_ref topic, const void* payload, size_t len,
int qos, bool retained, const properties& props=properties()) {
return std::make_shared<message>(std::move(topic), payload, len,
qos, retained, props);
}
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
*/
static ptr_t create(string_ref topic, const void* payload, size_t len) {
return std::make_shared<message>(std::move(topic), payload, len,
DFLT_QOS, DFLT_RETAINED);
}
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
static ptr_t create(string_ref topic, binary_ref payload, int qos, bool retained,
const properties& props=properties()) {
return std::make_shared<message>(std::move(topic), std::move(payload),
qos, retained, props);
}
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
*/
static ptr_t create(string_ref topic, binary_ref payload) {
return std::make_shared<message>(std::move(topic), std::move(payload),
DFLT_QOS, DFLT_RETAINED);
}
/**
* Constructs a message as a copy of the C message struct.
* @param topic The message topic
* @param msg A "C" MQTTAsync_message structure.
*/
static ptr_t create(string_ref topic, const MQTTAsync_message& msg) {
return std::make_shared<message>(std::move(topic), msg);
}
/**
* Copies another message to this one.
* @param rhs The other message.
* @return A reference to this message.
*/
message& operator=(const message& rhs);
/**
* Moves another message to this one.
* @param rhs The other message.
* @return A reference to this message.
*/
message& operator=(message&& rhs);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_message& c_struct() const { return msg_; }
#endif
/**
* Sets the topic string.
* @param topic The topic on which the message is published.
*/
void set_topic(string_ref topic) {
topic_ = topic ? std::move(topic) : string_ref(string());
}
/**
* Gets the topic reference for the message.
* @return The topic reference for the message.
*/
const string_ref& get_topic_ref() const { return topic_; }
/**
* Gets the topic for the message.
* @return The topic string for the message.
*/
const string& get_topic() const {
static const string EMPTY_STR;
return topic_ ? topic_.str() : EMPTY_STR;
}
/**
* Clears the payload, resetting it to be empty.
*/
void clear_payload();
/**
* Gets the payload reference.
*/
const binary_ref& get_payload_ref() const { return payload_; }
/**
* Gets the payload
*/
const binary& get_payload() const {
static const binary EMPTY_BIN;
return payload_ ? payload_.str() : EMPTY_BIN;
}
/**
* Gets the payload as a string
*/
const string& get_payload_str() const {
static const string EMPTY_STR;
return payload_ ? payload_.str() : EMPTY_STR;
}
/**
* Returns the quality of service for this message.
* @return The quality of service for this message.
*/
int get_qos() const { return msg_.qos; }
/**
* Returns whether or not this message might be a duplicate of one which
* has already been received.
* @return true this message might be a duplicate of one which
* has already been received, false otherwise
*/
bool is_duplicate() const { return to_bool(msg_.dup); }
/**
* Returns whether or not this message should be/was retained by the
* server.
* @return true if this message should be/was retained by the
* server, false otherwise.
*/
bool is_retained() const { return to_bool(msg_.retained); }
/**
* Sets the payload of this message to be the specified buffer.
* Note that this accepts copy or move operations:
* set_payload(buf);
* set_payload(std::move(buf));
* @param payload A buffer to use as the message payload.
*/
void set_payload(binary_ref payload);
/**
* Sets the payload of this message to be the specified byte array.
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
*/
void set_payload(const void* payload, size_t n) {
set_payload(binary_ref(static_cast<const binary_ref::value_type*>(payload), n));
}
/**
* Sets the quality of service for this message.
* @param qos The integer Quality of Service for the message
*/
void set_qos(int qos) {
validate_qos(qos);
msg_.qos = qos;
}
/**
* Determines if the QOS value is a valid one.
* @param qos The QOS value.
* @throw std::invalid_argument If the qos value is invalid.
*/
static void validate_qos(int qos) {
if (qos < 0 || qos > 2)
throw exception(MQTTASYNC_BAD_QOS, "Bad QoS");
}
/**
* Whether or not the publish message should be retained by the broker.
* @param retained @em true if the message should be retained by the
* broker, @em false if not.
*/
void set_retained(bool retained) { msg_.retained = to_int(retained); }
/**
* Gets the properties in the message.
* @return A const reference to the properties in the message.
*/
const properties& get_properties() const {
return props_;
}
/**
* Sets the properties in the message.
* @param props The properties to place into the message.
*/
void set_properties(const properties& props) {
props_ = props;
msg_.properties = props_.c_struct();
}
/**
* Moves the properties into the message.
* @param props The properties to move into the message.
*/
void set_properties(properties&& props) {
props_ = std::move(props);
msg_.properties = props_.c_struct();
}
/**
* Returns a string representation of this messages payload.
* @return A string representation of this messages payload.
*/
string to_string() const { return get_payload_str(); }
};
/** Smart/shared pointer to a message */
using message_ptr = message::ptr_t;
/** Smart/shared pointer to a const message */
using const_message_ptr = message::const_ptr_t;
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
*/
inline message_ptr make_message(string_ref topic, const void* payload, size_t len) {
return mqtt::message::create(std::move(topic), payload, len);
}
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
*/
inline message_ptr make_message(string_ref topic, const void* payload, size_t len,
int qos, bool retained) {
return mqtt::message::create(std::move(topic), payload, len, qos, retained);
}
/**
* Constructs a message with the specified buffer as a payload, and
* all other values set to defaults.
* @param topic The message topic
* @param payload A string to use as the message payload.
*/
inline message_ptr make_message(string_ref topic, binary_ref payload) {
return mqtt::message::create(std::move(topic), std::move(payload));
}
/**
* Constructs a message with the specified values.
* @param topic The message topic
* @param payload A buffer to use as the message payload.
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
*/
inline message_ptr make_message(string_ref topic, binary_ref payload,
int qos, bool retained) {
return mqtt::message::create(std::move(topic), std::move(payload), qos, retained);
}
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build messages.
*/
class message_ptr_builder
{
/** The underlying message */
message_ptr msg_;
public:
/** This class */
using self = message_ptr_builder;
/**
* Default constructor.
*/
message_ptr_builder() : msg_{ std::make_shared<message>() } {}
/**
* Sets the topic string.
* @param topic The topic on which the message is published.
*/
auto topic(string_ref topic) -> self& {
msg_->set_topic(topic);
return *this;
}
/**
* Sets the payload of this message to be the specified buffer.
* Note that this accepts copy or move operations:
* set_payload(buf);
* set_payload(std::move(buf));
* @param payload A buffer to use as the message payload.
*/
auto payload(binary_ref payload) -> self& {
msg_->set_payload(payload);
return *this;
}
/**
* Sets the payload of this message to be the specified byte array.
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
*/
auto payload(const void* payload, size_t n) -> self& {
msg_->set_payload(payload, n);
return *this;
}
/**
* Sets the quality of service for this message.
* @param qos The integer Quality of Service for the message
*/
auto qos(int qos) -> self& {
msg_->set_qos(qos);
return *this;
}
/**
* Whether or not the publish message should be retained by the broker.
* @param on @em true if the message should be retained by the broker, @em
* false if not.
*/
auto retained(bool on) -> self& {
msg_->set_retained(on);
return *this;
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(mqtt::properties&& props) -> self& {
msg_->set_properties(std::move(props));
return *this;
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(const mqtt::properties& props) -> self& {
msg_->set_properties(props);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
message_ptr finalize() { return msg_; }
};
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_message_h

View File

@@ -0,0 +1,30 @@
/////////////////////////////////////////////////////////////////////////////
/// @file platform.h
/// Paho MQTT platform-specific code
/// @date Nov 19, 2023
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_platform_h
#define __mqtt_platform_h
#include "mqtt/export.h"
#endif // __mqtt_platform_h

View File

@@ -0,0 +1,431 @@
/////////////////////////////////////////////////////////////////////////////
/// @file properties.h
/// Declaration of MQTT properties class
/// @date July 7, 2019
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2019-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_properties_h
#define __mqtt_properties_h
extern "C" {
#include "MQTTProperties.h"
}
#include "mqtt/types.h"
#include "mqtt/buffer_ref.h"
#include "mqtt/exception.h"
#include "mqtt/platform.h"
#include <tuple>
#include <initializer_list>
#include <iostream>
namespace mqtt {
/** A pair of strings as a tuple. */
using string_pair = std::tuple<string, string>;
/////////////////////////////////////////////////////////////////////////////
/**
* A single MQTT v5 property.
*/
class property
{
/** The underlying Paho C property struct. */
MQTTProperty prop_;
// Make a deep copy of the property struct into this one.
// For string properties, this allocates memory and copied the string(s)
void copy(const MQTTProperty& other);
public:
/**
* The integer codes for the different v5 properties.
*/
enum code {
PAYLOAD_FORMAT_INDICATOR = 1,
MESSAGE_EXPIRY_INTERVAL = 2,
CONTENT_TYPE = 3,
RESPONSE_TOPIC = 8,
CORRELATION_DATA = 9,
SUBSCRIPTION_IDENTIFIER = 11,
SESSION_EXPIRY_INTERVAL = 17,
ASSIGNED_CLIENT_IDENTIFIER = 18,
SERVER_KEEP_ALIVE = 19,
AUTHENTICATION_METHOD = 21,
AUTHENTICATION_DATA = 22,
REQUEST_PROBLEM_INFORMATION = 23,
WILL_DELAY_INTERVAL = 24,
REQUEST_RESPONSE_INFORMATION = 25,
RESPONSE_INFORMATION = 26,
SERVER_REFERENCE = 28,
REASON_STRING = 31,
RECEIVE_MAXIMUM = 33,
TOPIC_ALIAS_MAXIMUM = 34,
TOPIC_ALIAS = 35,
MAXIMUM_QOS = 36,
RETAIN_AVAILABLE = 37,
USER_PROPERTY = 38,
MAXIMUM_PACKET_SIZE = 39,
WILDCARD_SUBSCRIPTION_AVAILABLE = 40,
SUBSCRIPTION_IDENTIFIERS_AVAILABLE = 41,
SHARED_SUBSCRIPTION_AVAILABLE = 42
};
/**
* Create a numeric property.
* This can be a byte, or 2-byte, 4-byte, or variable byte integer.
* @param c The property code
* @param val The integer value for the property
*/
property(code c, int32_t val);
/**
* Create a numeric property.
* This can be a byte, or 2-byte, 4-byte, or variable byte integer.
* @param c The property code
* @param val The integer value for the property
*/
property(code c, uint32_t val) : property(c, int32_t(val)) {}
/**
* Create a string or binary property.
* @param c The property code
* @param val The value for the property
*/
property(code c, string_ref val);
/**
* Create a string pair property.
* @param c The property code
* @param name The string name for the property
* @param val The string value for the property
*/
property(code c, string_ref name, string_ref val);
/**
* Creates a property list from an C struct.
* @param cprop A C struct for a property list.
*/
explicit property(const MQTTProperty& cprop);
/**
* Moves a C struct into this property list.
* This takes ownership of any memory that the C struct is holding.
* @param cprop A C struct for a property list.
*/
explicit property(MQTTProperty&& cprop);
/**
* Copy constructor
* @param other The other property to copy into this one.
*/
property(const property& other);
/**
* Move constructor.
* @param other The other property that is moved into this one.
*/
property(property&& other);
/**
* Destructor
*/
~property();
/**
* Copy assignment.
* @param rhs Another property list to copy into this one.
* @return A reference to this object.
*/
property& operator=(const property& rhs);
/**
* Move assignment.
* @param rhs Another property list to move into this one.
* @return A reference to this object.
*/
property& operator=(property&& rhs);
/**
* Gets the underlying C property struct.
* @return A const reference to the underlying C property
* struct.
*/
const MQTTProperty& c_struct() const { return prop_; }
/**
* Gets the property type (identifier).
* @return The code for the property type.
*/
code type() const { return code(prop_.identifier); }
/**
* Gets a printable name for the property type.
* @return A printable name for the property type.
*/
const char* type_name() const {
return ::MQTTPropertyName(prop_.identifier);
}
};
/**
* Extracts the value from the property as the specified type.
* @return The value from the property as the specified type.
*/
template <typename T>
inline T get(const property&) { throw bad_cast(); }
/**
* Extracts the value from the property as an unsigned 8-bit integer.
* @return The value from the property as an unsigned 8-bit integer.
*/
template <>
inline uint8_t get<uint8_t>(const property& prop) {
return (uint8_t) prop.c_struct().value.byte;
}
/**
* Extracts the value from the property as an unsigned 16-bit integer.
* @return The value from the property as an unsigned 16-bit integer.
*/
template <>
inline uint16_t get<uint16_t>(const property& prop) {
return (uint16_t) prop.c_struct().value.integer2;
}
/**
* Extracts the value from the property as a signed 16-bit integer.
* @return The value from the property as a signed 16-bit integer.
* @deprecated All integer properties are unsigned. Use
* `get<uint16_t>()`
*/
template <>
inline int16_t get<int16_t>(const property& prop) {
return (int16_t) prop.c_struct().value.integer2;
}
/**
* Extracts the value from the property as an unsigned 32-bit integer.
* @return The value from the property as an unsigned 32-bit integer.
*/
template <>
inline uint32_t get<uint32_t>(const property& prop) {
return (uint32_t) prop.c_struct().value.integer4;
}
/**
* Extracts the value from the property as a signed 32-bit integer.
* @return The value from the property as a signed 32-bit integer.
* @deprecated All integer properties are unsigned. Use
* `get<uint32_t>()`
*/
template <>
inline int32_t get<int32_t>(const property& prop) {
return (int32_t) prop.c_struct().value.integer4;
}
/**
* Extracts the value from the property as a string.
* @return The value from the property as a string.
*/
template <>
inline string get<string>(const property& prop) {
return (!prop.c_struct().value.data.data) ? string()
: string(prop.c_struct().value.data.data, prop.c_struct().value.data.len);
}
/**
* Extracts the value from the property as a pair of strings.
* @return The value from the property as a pair of strings.
*/
template <>
inline string_pair get<string_pair>(const property& prop) {
string name = (!prop.c_struct().value.data.data) ? string()
: string(prop.c_struct().value.data.data, prop.c_struct().value.data.len);
string value = (!prop.c_struct().value.value.data) ? string()
: string(prop.c_struct().value.value.data, prop.c_struct().value.value.len);
return std::make_tuple(std::move(name), std::move(value));
}
/////////////////////////////////////////////////////////////////////////////
/**
* MQTT v5 property list.
*
* A collection of properties that can be added to outgoing packets or
* retrieved from incoming packets.
*/
class properties
{
/** The default C struct */
PAHO_MQTTPP_EXPORT static const MQTTProperties DFLT_C_STRUCT;
/** The underlying C properties struct */
MQTTProperties props_;
template<typename T>
friend T get(const properties& props, property::code propid, size_t idx);
template<typename T>
friend T get(const properties& props, property::code propid);
public:
/**
* Default constructor.
* Creates an empty properties list.
*/
properties();
/**
* Copy constructor.
* @param other The property list to copy.
*/
properties(const properties& other)
: props_(::MQTTProperties_copy(&other.props_)) {}
/**
* Move constructor.
* @param other The property list to move to this one.
*/
properties(properties&& other) : props_(other.props_) {
std::memset(&other.props_, 0, sizeof(MQTTProperties));
}
/**
* Creates a list of properties from a C struct.
* @param cprops The c struct of properties
*/
properties(const MQTTProperties& cprops) {
props_ = ::MQTTProperties_copy(&cprops);
}
/**
* Constructs from a list of property objects.
* @param props An initializer list of property objects.
*/
properties(std::initializer_list<property> props);
/**
* Destructor.
*/
~properties() { ::MQTTProperties_free(&props_); }
/**
* Gets a reference to the underlying C properties structure.
* @return A const reference to the underlying C properties structure.
*/
const MQTTProperties& c_struct() const { return props_; }
/**
* Copy assignment.
* @param rhs The other property list to copy into this one
* @return A reference to this object.
*/
properties& operator=(const properties& rhs);
/**
* Move assignment.
* @param rhs The property list to move to this one.
* @return A reference to this object.
*/
properties& operator=(properties&& rhs);
/**
* Determines if the property list is empty.
* @return @em true if there are no properties in the list, @em false if
* the list contains any items.
*/
bool empty() const { return props_.count == 0; }
/**
* Gets the numbers of property items in the list.
* @return The number of property items in the list.
*/
size_t size() const { return size_t(props_.count); }
/**
* Adds a property to the list.
* @param prop The property to add to the list.
*/
void add(const property& prop) {
::MQTTProperties_add(&props_, &prop.c_struct());
}
/**
* Removes all the items from the property list.
*/
void clear() {
::MQTTProperties_free(&props_);
}
/**
* Determines if the list contains a specific property.
* @param propid The property ID (code).
* @return @em true if the list contains the property, @em false if not.
*/
bool contains(property::code propid) const {
return ::MQTTProperties_hasProperty(const_cast<MQTTProperties*>(&props_),
MQTTPropertyCodes(propid)) != 0;
}
/**
* Get the number of properties in the list with the specified property
* ID.
*
* Most properties can exist only once. User properties and subscription
* ID's can exist more than once.
*
* @param propid The property ID (code).
* @return The number of properties in the list with the specified ID.
*/
size_t count(property::code propid) const {
return size_t(::MQTTProperties_propertyCount(
const_cast<MQTTProperties*>(&props_), MQTTPropertyCodes(propid)));
}
/**
* Gets the property with the specified ID.
*
* @param propid The property ID (code).
* @param idx Which instance of the property to retrieve, if there are
* more than one.
* @return The requested property
*/
property get(property::code propid, size_t idx=0);
};
// --------------------------------------------------------------------------
/**
* Retrieves a single value from a property list for when there may be
* multiple identical property ID's.
* @tparam T The type of the value to retrieve
* @param props The property list
* @param propid The property ID code for the desired value.
* @param idx Index of the desired property ID
* @return The requested value of type T
*/
template<typename T>
inline T get(const properties& props, property::code propid, size_t idx)
{
MQTTProperty* prop = MQTTProperties_getPropertyAt(
const_cast<MQTTProperties*>(&props.c_struct()),
MQTTPropertyCodes(propid), int(idx));
if (!prop)
throw bad_cast();
return get<T>(property(*prop));
}
/**
* Retrieves a single value from a property list.
* @tparam T The type of the value to retrieve
* @param props The property list
* @param propid The property ID code for the desired value.
* @return The requested value of type T
*/
template<typename T>
inline T get(const properties& props, property::code propid)
{
return get<T>(props, propid, 0);
}
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_properties_h

Some files were not shown because too many files have changed in this diff Show More