diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 00000000..91f357d9 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,57 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "clang_cl_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "variables": [ + { + "name": "WITH_TLS", + "value": "False", + "type": "BOOL" + }, + { + "name": "WITH_CLIENTS", + "value": "False", + "type": "BOOL" + }, + { + "name": "WITH_WEBSOCKETS", + "value": "False", + "type": "BOOL" + }, + { + "name": "WITH_THREADING", + "value": "False", + "type": "BOOL" + }, + { + "name": "DOCUMENTATION", + "value": "False", + "type": "BOOL" + }, + { + "name": "CJSON_DIR", + "value": "C:/Users/d.kuhn/Projekte/cJSON/", + "type": "STRING" + }, + { + "name": "CJSON_INCLUDE_DIR", + "value": "C:/Users/d.kuhn/Projekte/cJSON/", + "type": "PATH" + }, + { + "name": "CJSON_LIBRARY", + "value": "C:/Users/d.kuhn/Projekte/cJSON/cjson/out/build/x64-Debug/cjson.lib", + "type": "FILEPATH" + } + ] + } + ] +} \ No newline at end of file diff --git a/config.mk b/config.mk index 6e0139c1..bc9aeefe 100644 --- a/config.mk +++ b/config.mk @@ -67,7 +67,7 @@ WITH_SYSTEMD:=no WITH_SRV:=no # Build with websockets support on the broker. -WITH_WEBSOCKETS:=no +WITH_WEBSOCKETS:=yes # Use elliptic keys in broker WITH_EC:=yes diff --git a/mosquitto.conf b/mosquitto.conf index 10b0406e..6e5f7c69 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -902,3 +902,7 @@ # given multiple times, all of the files from the first instance will be # processed before the next instance. See the man page for examples. #include_dir + +# plugin C:\Users\d.kuhn\Projekte\mosquitto\out\build\x64-Debug\plugins\payload-modification\mosquitto_payload_modification.dll + +plugin C:\Users\d.kuhn\Projekte\mosquitto\out\build\x64-Debug\plugins\wamo\wamo.dll \ No newline at end of file diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index c9aa03df..e7469bcd 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -3,3 +3,4 @@ if(NOT WIN32) add_subdirectory(message-timestamp) endif(NOT WIN32) add_subdirectory(payload-modification) +add_subdirectory(wamo) diff --git a/plugins/wamo/CMakeLists.txt b/plugins/wamo/CMakeLists.txt new file mode 100644 index 00000000..470eb681 --- /dev/null +++ b/plugins/wamo/CMakeLists.txt @@ -0,0 +1,51 @@ +# include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include +# ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH}) +# link_directories(${mosquitto_SOURCE_DIR}) +# +# add_library(wamo MODULE wamo.c "json_help.c" "json_help.h") +# set_target_properties(wamo PROPERTIES +# POSITION_INDEPENDENT_CODE 1 +# ) +# set_target_properties(wamo PROPERTIES PREFIX "") +# if(WIN32) +# target_link_libraries(wamo mosquitto ${CJSON_LIBRARIES}) +# endif(WIN32) + +# Don't install, these are example plugins only. +#install(TARGETS wamo RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + + +if (CJSON_FOUND ) + add_definitions("-DWITH_CJSON") + + set( CLIENT_INC ${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include + ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${mosquitto_SOURCE_DIR}/deps + ${mosquitto_SOURCE_DIR}/src ${CJSON_INCLUDE_DIRS} ) + + set( CLIENT_DIR ${mosquitto_BINARY_DIR}/lib ${CJSON_DIR}) + + include_directories(${CLIENT_INC}) + link_directories(${CLIENT_DIR} ${mosquitto_SOURCE_DIR}) + + add_library(wamo MODULE + json_help.c + json_help.h + wamo.c) + + set_target_properties(wamo PROPERTIES + POSITION_INDEPENDENT_CODE 1 + ) + set_target_properties(wamo PROPERTIES PREFIX "") + + target_link_libraries(wamo ${CJSON_LIBRARIES}) + if(WIN32) + target_link_libraries(wamo mosquitto) + install(TARGETS wamo + DESTINATION "${CMAKE_INSTALL_BINDIR}") + else() + install(TARGETS wamo + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif() + +endif() diff --git a/plugins/wamo/Makefile b/plugins/wamo/Makefile new file mode 100644 index 00000000..24beff4d --- /dev/null +++ b/plugins/wamo/Makefile @@ -0,0 +1,53 @@ +include ../../config.mk + +.PHONY : all binary check clean reallyclean test install uninstall + +PLUGIN_NAME=wamo +LOCAL_CPPFLAGS=-I../../src/ -DWITH_CJSON + +OBJS= \ + + json_help.o \ + wamo.o + + +ifeq ($(WITH_CJSON),yes) +ALL_DEPS:= binary +else +ALL_DEPS:= +endif +else +ALL_DEPS:= +endif + +all : ${ALL_DEPS} +binary : ${PLUGIN_NAME}.so + +${PLUGIN_NAME}.so : ${OBJS} + ${CROSS_COMPILE}${CC} $(PLUGIN_LDFLAGS) -fPIC -shared $^ -o $@ -lcjson + + + +json_help.o : json_help.c + ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ + +wamo.o : wamo.c + ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ + + +reallyclean : clean +clean: + -rm -f *.o ${PLUGIN_NAME}.so *.gcda *.gcno + +check: test +test: + +install: all +ifeq ($(WITH_CJSON),yes) + $(INSTALL) -d "${DESTDIR}$(libdir)" + $(INSTALL) ${STRIP_OPTS} ${PLUGIN_NAME}.so "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" +endif +endif + +uninstall : + -rm -f "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" diff --git a/plugins/wamo/json_help.c b/plugins/wamo/json_help.c new file mode 100644 index 00000000..8c06a898 --- /dev/null +++ b/plugins/wamo/json_help.c @@ -0,0 +1,182 @@ +/* + +Contributors: + Dominik Kuhn - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "json_help.h" +#include "mosquitto.h" +#include "mosquitto_broker.h" + +int json_create_array(cJSON* json, const char* name) { + cJSON* jtmp; + + jtmp = cJSON_GetObjectItem(json, name); + if (jtmp) { + if (cJSON_IsArray(jtmp) == true) { + return MOSQ_ERR_INVAL; + } + else { + cJSON_AddArrayToObject(json, name); + } + + } + else { + cJSON_AddArrayToObject(json, name); + } + + return MOSQ_ERR_SUCCESS; + +} + +int json_add_id_to_array(cJSON* json, const char* name, const char * id) +{ + cJSON* jtmp; + cJSON* tid; + + jtmp = cJSON_GetObjectItem(json, name); + if (jtmp) { + if (cJSON_IsArray(jtmp) == true) { + tid = cJSON_CreateString(id); + cJSON_AddItemToArray(jtmp, tid); + } + else { + return MOSQ_ERR_INVAL; + } + + } + else { + return MOSQ_ERR_INVAL; + } + + return MOSQ_ERR_SUCCESS; + +} + +int json_del_id_from_array(cJSON* json, const char* name, const char* id) +{ + cJSON* jtmp = NULL; + cJSON* tid = NULL; + cJSON* detached_item = NULL; + + int array_index = 0; + + jtmp = cJSON_GetObjectItem(json, name); + if (jtmp) { + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: deleting item from array .. debug 1"); + if (cJSON_IsArray(jtmp) == true) { + // cJSON_DeleteItemFromObject(json, name); + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: deleting item from array .. debug 2"); + cJSON_ArrayForEach(tid, jtmp) + { + + if ( strcmp( cJSON_GetStringValue(tid),id) == 0) + { + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: deleting item from array .. debug 3"); + detached_item = cJSON_DetachItemFromArray(jtmp, array_index); + + } + else + { + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: deleting item from array .. debug 4"); + } + array_index++; + } + // cJSON_ReplaceItemInObject(json, name, jtmp); + // cJSON_AddItemToObject(json, id, jtmp); + } + else { + return MOSQ_ERR_INVAL; + } + + } + else { + return MOSQ_ERR_INVAL; + } + + return MOSQ_ERR_SUCCESS; + +} + +int json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value) +{ + cJSON *jtmp; + + if(optional == true){ + *value = default_value; + } + + jtmp = cJSON_GetObjectItem(json, name); + if(jtmp){ + if(cJSON_IsBool(jtmp) == false){ + return MOSQ_ERR_INVAL; + } + *value = cJSON_IsTrue(jtmp); + }else{ + if(optional == false){ + return MOSQ_ERR_INVAL; + } + } + return MOSQ_ERR_SUCCESS; +} + + +int json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value) +{ + cJSON *jtmp; + + if(optional == true){ + *value = default_value; + } + + jtmp = cJSON_GetObjectItem(json, name); + if(jtmp){ + if(cJSON_IsNumber(jtmp) == false){ + return MOSQ_ERR_INVAL; + } + *value = jtmp->valueint; + }else{ + if(optional == false){ + return MOSQ_ERR_INVAL; + } + } + return MOSQ_ERR_SUCCESS; +} + + +int json_get_string(cJSON *json, const char *name, char **value, bool optional) +{ + cJSON *jtmp; + + *value = NULL; + + jtmp = cJSON_GetObjectItem(json, name); + if(jtmp){ + if(cJSON_IsString(jtmp) == false){ + return MOSQ_ERR_INVAL; + } + *value = jtmp->valuestring; + }else{ + if(optional == false){ + return MOSQ_ERR_INVAL; + } + } + return MOSQ_ERR_SUCCESS; +} + + +cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number) +{ + char buf[30]; + + snprintf(buf, sizeof(buf), "%d", number); + return cJSON_AddRawToObject(object, name, buf); +} diff --git a/plugins/wamo/json_help.h b/plugins/wamo/json_help.h new file mode 100644 index 00000000..f11eea3e --- /dev/null +++ b/plugins/wamo/json_help.h @@ -0,0 +1,23 @@ +#ifndef JSON_HELP_H +#define JSON_HELP_H +/* + +Contributors: + Dominik Kuhn Light - initial implementation and documentation. +*/ +#include +#include + +int json_create_array(cJSON* json, const char* name); +int json_add_id_to_array(cJSON* json, const char* name, const char* id); +int json_del_id_from_array(cJSON* json, const char* name, const char* id); + +/* "optional==false" can also be taken to mean "only return success if the key exists and is valid" */ +int json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value); +int json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value); +int json_get_string(cJSON *json, const char *name, char **value, bool optional); + +cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number); +cJSON *cJSON_CreateInt(int num); + +#endif diff --git a/plugins/wamo/wamo.c b/plugins/wamo/wamo.c new file mode 100644 index 00000000..a5740a04 --- /dev/null +++ b/plugins/wamo/wamo.c @@ -0,0 +1,167 @@ +/* +Copyright (c) 2020 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.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. + +SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +/* + * This is an *example* plugin which demonstrates how to modify the payload of + * a message after it is received by the broker and before it is sent on to + * other clients. + * + * You should be very sure of what you are doing before making use of this feature. + * + * Compile with: + * gcc -I -fPIC -shared mosquitto_payload_modification.c -o mosquitto_payload_modification.so + * + * Use in config with: + * + * plugin /path/to/mosquitto_payload_modification.so + * + * Note that this only works on Mosquitto 2.0 or later. + */ +#include +#include + +#include "mosquitto_broker.h" +#include "mosquitto_plugin.h" +#include "mosquitto.h" +#include "mqtt_protocol.h" + +#include "json_help.h" + +#define UNUSED(A) (void)(A) + +static mosquitto_plugin_id_t* mosq_pid = NULL; +static cJSON* subscribedTopics = NULL; + +static int callback_control(int event, void* event_data, void* userdata) +{ + struct mosquitto_evt_acl_check* ed = event_data; + + UNUSED(event); + UNUSED(userdata); + + const char * client_id = mosquitto_client_id(ed->client); + const char* topic = ed->topic; + const int access = ed->access; + + + + if (access == MOSQ_ACL_SUBSCRIBE) { + json_create_array(subscribedTopics, topic); + json_add_id_to_array(subscribedTopics, topic, client_id); + char* json_string = cJSON_Print(subscribedTopics); + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: client with id %s subscribed to topic %s", client_id, topic); + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: subscribed topics %s", json_string); + } + else if (access == MOSQ_ACL_UNSUBSCRIBE) { + json_del_id_from_array(subscribedTopics, topic, client_id); + char* json_string = cJSON_Print(subscribedTopics); + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: client with id %s unscribed to topic %s", client_id, topic); + mosquitto_log_printf(MOSQ_LOG_INFO, "wamo: subscribed topics %s", json_string); + } + + + + return MOSQ_ERR_SUCCESS; +} + + +static int callback_message(int event, void* event_data, void* userdata) +{ + struct mosquitto_evt_message* ed = event_data; + char* new_payload; + uint32_t new_payloadlen; + + UNUSED(event); + UNUSED(userdata); + + /* This simply adds "hello " to the front of every payload. You can of + * course do much more complicated message processing if needed. */ + + /* Calculate the length of our new payload */ + new_payloadlen = ed->payloadlen + (uint32_t)strlen("hello ") + 1; + + /* Allocate some memory - use + * mosquitto_calloc/mosquitto_malloc/mosquitto_strdup when allocating, to + * allow the broker to track memory usage */ + new_payload = mosquitto_calloc(1, new_payloadlen); + if (new_payload == NULL) { + return MOSQ_ERR_NOMEM; + } + + /* Print "hello " to the payload */ + snprintf(new_payload, new_payloadlen, "hello "); + memcpy(new_payload + (uint32_t)strlen("hello "), ed->payload, ed->payloadlen); + + /* Assign the new payload and payloadlen to the event data structure. You + * must *not* free the original payload, it will be handled by the + * broker. */ + ed->payload = new_payload; + ed->payloadlen = new_payloadlen; + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_plugin_version(int supported_version_count, const int* supported_versions) +{ + int i; + + for (i = 0; i < supported_version_count; i++) { + if (supported_versions[i] == 5) { + return 5; + } + } + return -1; +} + +int mosquitto_plugin_init(mosquitto_plugin_id_t* identifier, void** user_data, struct mosquitto_opt* opts, int opt_count) +{ + int rc; + + UNUSED(user_data); + + mosq_pid = identifier; + + subscribedTopics = cJSON_CreateObject(); + + rc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_ACL_CHECK, callback_control, NULL, NULL); + if (rc == MOSQ_ERR_ALREADY_EXISTS) { + mosquitto_log_printf(MOSQ_LOG_ERR, "Error: WaMo plugin can currently only be loaded once."); + mosquitto_log_printf(MOSQ_LOG_ERR, "Note that this was previously incorrectly allowed but could cause problems with duplicate entries in the config."); + return rc; + } + else if (rc == MOSQ_ERR_NOMEM) { + mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory."); + return rc; + } + else if (rc != MOSQ_ERR_SUCCESS) { + return rc; + } + + return MOSQ_ERR_SUCCESS; + + +} + +int mosquitto_plugin_cleanup(void* user_data, struct mosquitto_opt* opts, int opt_count) +{ + UNUSED(user_data); + UNUSED(opts); + UNUSED(opt_count); + + return mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_ACL_CHECK, callback_control, NULL); +}