diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a6b2c01891..39a12d1d3a 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -2,7 +2,7 @@ include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib ${STDBOOL_H_PATH} ${STDINT_H_PATH}) link_directories(${mosquitto_BINARY_DIR}/lib) -set(shared_src client_shared.c client_shared.h) +set(shared_src client_shared.c client_shared.h client_props.c) if (${WITH_SRV} STREQUAL ON) add_definitions("-DWITH_SRV") diff --git a/client/Makefile b/client/Makefile index 02ac608726..4ffc46b153 100644 --- a/client/Makefile +++ b/client/Makefile @@ -14,10 +14,10 @@ static_pub : pub_client.o client_shared.o ../lib/libmosquitto.a static_sub : sub_client.o sub_client_output.o client_shared.o ../lib/libmosquitto.a ${CROSS_COMPILE}${CC} $^ -o mosquitto_sub ${CLIENT_LDFLAGS} -lssl -lcrypto -lpthread -mosquitto_pub : pub_client.o client_shared.o +mosquitto_pub : pub_client.o client_shared.o client_props.o ${CROSS_COMPILE}${CC} $^ -o $@ ${CLIENT_LDFLAGS} -mosquitto_sub : sub_client.o sub_client_output.o client_shared.o +mosquitto_sub : sub_client.o sub_client_output.o client_shared.o client_props.o ${CROSS_COMPILE}${CC} $^ -o $@ ${CLIENT_LDFLAGS} pub_client.o : pub_client.c ../lib/libmosquitto.so.${SOVERSION} @@ -32,6 +32,9 @@ sub_client_output.o : sub_client_output.c ../lib/libmosquitto.so.${SOVERSION} client_shared.o : client_shared.c client_shared.h ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} +client_props.o : client_props.c client_shared.h + ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} + ../lib/libmosquitto.so.${SOVERSION} : $(MAKE) -C ../lib diff --git a/client/client_props.c b/client/client_props.c new file mode 100644 index 0000000000..908fbb0f73 --- /dev/null +++ b/client/client_props.c @@ -0,0 +1,185 @@ +/* +Copyright (c) 2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#include +#else +#include +#include +#define snprintf sprintf_s +#define strncasecmp _strnicmp +#endif + +#include "mosquitto.h" +#include "mqtt_protocol.h" +#include "client_shared.h" + +enum prop_type +{ + PROP_TYPE_BYTE, + PROP_TYPE_INT16, + PROP_TYPE_INT32, + PROP_TYPE_BINARY, + PROP_TYPE_STRING, + PROP_TYPE_STRING_PAIR +}; + +/* This parses property inputs. It should work for any command type, but is limited at the moment. + * + * Format: + * + * command property value + * command property key value + * + * Example: + * + * publish message-expiry-interval 32 + * connect user-property key value + */ + +int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx) +{ + char *cmdname = NULL, *propname = NULL; + char *key = NULL, *value = NULL; + int cmd, identifier, type; + mosquitto_property **proplist; + int rc; + + /* idx now points to "command" */ + if((*idx)+2 > argc-1){ + /* Not enough args */ + fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n"); + return MOSQ_ERR_INVAL; + } + + cmdname = argv[*idx]; + if(mosquitto_string_to_command(cmdname, &cmd)){ + fprintf(stderr, "Error: Invalid command given in --property argument.\n\n"); + return MOSQ_ERR_INVAL; + } + + propname = argv[(*idx)+1]; + if(mosquitto_string_to_property_info(propname, &identifier, &type)){ + fprintf(stderr, "Error: Invalid property name given in --property argument.\n\n"); + return MOSQ_ERR_INVAL; + } + + if(identifier == MQTT_PROP_USER_PROPERTY){ + if((*idx)+3 > argc-1){ + /* Not enough args */ + fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n"); + return MOSQ_ERR_INVAL; + } + + key = argv[(*idx)+2]; + value = argv[(*idx)+3]; + (*idx) += 3; + }else{ + value = argv[(*idx)+2]; + (*idx) += 2; + } + + + switch(cmd){ + case CMD_CONNECT: + proplist = &cfg->connect_props; + break; + + case CMD_PUBLISH: + if(identifier == MQTT_PROP_SUBSCRIPTION_IDENTIFIER){ + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_INVAL; + } + proplist = &cfg->publish_props; + break; + + case CMD_SUBSCRIBE: + if(identifier != MQTT_PROP_USER_PROPERTY){ + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_NOT_SUPPORTED; + } + proplist = &cfg->subscribe_props; + break; + + case CMD_UNSUBSCRIBE: + proplist = &cfg->subscribe_props; + break; + + case CMD_DISCONNECT: + proplist = &cfg->disconnect_props; + break; + + case CMD_AUTH: + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_NOT_SUPPORTED; + + case CMD_WILL: + proplist = &cfg->will_props; + break; + + case CMD_PUBACK: + case CMD_PUBREC: + case CMD_PUBREL: + case CMD_PUBCOMP: + case CMD_SUBACK: + case CMD_UNSUBACK: + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_NOT_SUPPORTED; + + default: + return MOSQ_ERR_INVAL; + } + + switch(type){ + case MQTT_PROP_TYPE_BYTE: + rc = mosquitto_property_add_byte(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_INT16: + rc = mosquitto_property_add_int16(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_INT32: + rc = mosquitto_property_add_int32(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_VARINT: + rc = mosquitto_property_add_varint(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_BINARY: + rc = mosquitto_property_add_binary(proplist, identifier, value, strlen(value)); + break; + case MQTT_PROP_TYPE_STRING: + rc = mosquitto_property_add_string(proplist, identifier, value); + break; + case MQTT_PROP_TYPE_STRING_PAIR: + rc = mosquitto_property_add_string_pair(proplist, identifier, key, value); + break; + default: + return MOSQ_ERR_INVAL; + } + if(rc){ + fprintf(stderr, "Error adding property %s %d\n", propname, type); + return rc; + } + return MOSQ_ERR_SUCCESS; +} + diff --git a/client/client_shared.c b/client/client_shared.c index 253ef1619f..aa2529aa51 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -179,6 +179,12 @@ void client_config_cleanup(struct mosq_config *cfg) free(cfg->socks5_username); free(cfg->socks5_password); #endif + mosquitto_property_free_all(&cfg->connect_props); + mosquitto_property_free_all(&cfg->publish_props); + mosquitto_property_free_all(&cfg->subscribe_props); + mosquitto_property_free_all(&cfg->unsubscribe_props); + mosquitto_property_free_all(&cfg->disconnect_props); + mosquitto_property_free_all(&cfg->will_props); } int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]) @@ -877,6 +883,12 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c goto unknown_option; } cfg->verbose = 1; + }else if(!strcmp(argv[i], "-y") || !strcmp(argv[i], "--property")){ + i++; + if(cfg_parse_property(cfg, argc, argv, &i)){ + return 1; + } + cfg->protocol_version = MQTT_PROTOCOL_V5; }else{ goto unknown_option; } @@ -1006,10 +1018,10 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) if(cfg->use_srv){ rc = mosquitto_connect_srv(mosq, cfg->host, cfg->keepalive, cfg->bind_address); }else{ - rc = mosquitto_connect_bind(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address); + rc = mosquitto_connect_bind_with_properties(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props); } #else - rc = mosquitto_connect_bind(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address); + rc = mosquitto_connect_bind_with_properties(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props); #endif if(rc>0){ if(!cfg->quiet){ diff --git a/client/client_shared.h b/client/client_shared.h index 0676229325..18adcf3f26 100644 --- a/client/client_shared.h +++ b/client/client_shared.h @@ -92,6 +92,12 @@ struct mosq_config { char *socks5_username; char *socks5_password; #endif + mosquitto_property *connect_props; + mosquitto_property *publish_props; + mosquitto_property *subscribe_props; + mosquitto_property *unsubscribe_props; + mosquitto_property *disconnect_props; + mosquitto_property *will_props; }; int client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]); @@ -100,4 +106,6 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg); int client_id_generate(struct mosq_config *cfg, const char *id_base); int client_connect(struct mosquitto *mosq, struct mosq_config *cfg); +int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx); + #endif diff --git a/client/pub_client.c b/client/pub_client.c index 9fb38889c0..ffeb2ec185 100644 --- a/client/pub_client.c +++ b/client/pub_client.c @@ -54,6 +54,7 @@ static char *username = NULL; static char *password = NULL; static bool disconnect_sent = false; static bool quiet = false; +static struct mosq_config cfg; void my_connect_callback(struct mosquitto *mosq, void *obj, int result) { @@ -64,10 +65,10 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result) case MSGMODE_CMD: case MSGMODE_FILE: case MSGMODE_STDIN_FILE: - rc = mosquitto_publish(mosq, &mid_sent, topic, msglen, message, qos, retain); + rc = mosquitto_publish_with_properties(mosq, &mid_sent, topic, msglen, message, qos, retain, cfg.publish_props); break; case MSGMODE_NULL: - rc = mosquitto_publish(mosq, &mid_sent, topic, 0, NULL, qos, retain); + rc = mosquitto_publish_with_properties(mosq, &mid_sent, topic, 0, NULL, qos, retain, cfg.publish_props); break; case MSGMODE_STDIN_LINE: status = STATUS_CONNACK_RECVD; @@ -230,6 +231,8 @@ void print_usage(void) #ifdef WITH_SOCKS printf(" [--proxy socks-url]\n"); #endif + printf(" [--property command identifier value]\n"); + printf(" [-y command identifier value]\n"); printf(" mosquitto_pub --help\n\n"); printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n"); printf(" the client communicates over.\n"); @@ -258,6 +261,7 @@ void print_usage(void) printf(" -u : provide a username\n"); printf(" -V : specify the version of the MQTT protocol to use when connecting.\n"); printf(" Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\n"); + printf(" -y,--property : Add MQTT v5 properties. See the documentation for more details.\n"); printf(" --help : display this message.\n"); printf(" --quiet : don't print error messages.\n"); printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n"); @@ -290,12 +294,11 @@ void print_usage(void) printf(" socks5h://[username[:password]@]hostname[:port]\n"); printf(" Only \"none\" and \"username\" authentication is supported.\n"); #endif - printf("\nSee http://mosquitto.org/ for more information.\n\n"); + printf("\nSee https://mosquitto.org/ for more information.\n\n"); } int main(int argc, char *argv[]) { - struct mosq_config cfg; struct mosquitto *mosq = NULL; int rc; int rc2; @@ -413,7 +416,7 @@ int main(int argc, char *argv[]) buf_len_actual = strlen(buf); if(buf[buf_len_actual-1] == '\n'){ buf[buf_len_actual-1] = '\0'; - rc2 = mosquitto_publish(mosq, &mid_sent, topic, buf_len_actual-1, buf, qos, retain); + rc2 = mosquitto_publish_with_properties(mosq, &mid_sent, topic, buf_len_actual-1, buf, qos, retain, cfg.publish_props); if(rc2){ if(!quiet) fprintf(stderr, "Error: Publish returned %d, disconnecting.\n", rc2); mosquitto_disconnect(mosq); diff --git a/client/sub_client.c b/client/sub_client.c index 4495849dcb..5e9a972db3 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -96,7 +96,7 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flag cfg = (struct mosq_config *)obj; if(!result){ - mosquitto_subscribe_multiple(mosq, NULL, cfg->topic_count, cfg->topics, cfg->qos); + mosquitto_subscribe_multiple(mosq, NULL, cfg->topic_count, cfg->topics, cfg->qos, cfg->subscribe_props); for(i=0; iunsub_topic_count; i++){ mosquitto_unsubscribe(mosq, NULL, cfg->unsub_topics[i]); @@ -165,6 +165,8 @@ void print_usage(void) #ifdef WITH_SOCKS printf(" [--proxy socks-url]\n"); #endif + printf(" [-y command identifier value]\n"); + printf(" [--property command identifier value]\n"); printf(" mosquitto_sub --help\n\n"); printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n"); printf(" the client communicates over.\n"); @@ -198,6 +200,7 @@ void print_usage(void) #ifndef WIN32 printf(" -W : Specifies a timeout in seconds how long to process incoming MQTT messages.\n"); #endif + printf(" -y, --property : Add MQTT v5 properties. See the documentation for more details.\n"); printf(" --help : display this message.\n"); printf(" --quiet : don't print error messages.\n"); printf(" --retained-only : only handle messages with the retained flag set, and exit when the\n"); diff --git a/lib/actions.c b/lib/actions.c index 7a48f3cc26..c6bdef5c44 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -29,6 +29,11 @@ and the Eclipse Distribution License is available at int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain) +{ + return mosquitto_publish_with_properties(mosq, mid, topic, payloadlen, payload, qos, retain, NULL); +} + +int mosquitto_publish_with_properties(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties) { struct mosquitto_message_all *message; uint16_t local_mid; @@ -49,7 +54,7 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p } if(qos == 0){ - return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false, NULL); + return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false, properties); }else{ message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!message) return MOSQ_ERR_NOMEM; @@ -87,7 +92,7 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p message->state = mosq_ms_wait_for_pubrec; } pthread_mutex_unlock(&mosq->out_message_mutex); - return send__publish(mosq, message->msg.mid, message->msg.topic, message->msg.payloadlen, message->msg.payload, message->msg.qos, message->msg.retain, message->dup, NULL); + return send__publish(mosq, message->msg.mid, message->msg.topic, message->msg.payloadlen, message->msg.payload, message->msg.qos, message->msg.retain, message->dup, properties); }else{ message->state = mosq_ms_invalid; pthread_mutex_unlock(&mosq->out_message_mutex); @@ -98,6 +103,11 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos) +{ + return mosquitto_subscribe_with_properties(mosq, mid, sub, qos, NULL); +} + +int mosquitto_subscribe_with_properties(struct mosquitto *mosq, int *mid, const char *sub, int qos, const mosquitto_property *properties) { if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; @@ -105,11 +115,11 @@ int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int q if(mosquitto_sub_topic_check(sub)) return MOSQ_ERR_INVAL; if(mosquitto_validate_utf8(sub, strlen(sub))) return MOSQ_ERR_MALFORMED_UTF8; - return send__subscribe(mosq, mid, 1, (char *const *const)&sub, qos); + return send__subscribe(mosq, mid, 1, (char *const *const)&sub, qos, properties); } -int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos) +int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, const mosquitto_property *properties) { int i; @@ -122,7 +132,7 @@ int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count if(mosquitto_validate_utf8(sub[i], strlen(sub[i]))) return MOSQ_ERR_MALFORMED_UTF8; } - return send__subscribe(mosq, mid, sub_count, sub, qos); + return send__subscribe(mosq, mid, sub_count, sub, qos, properties); } diff --git a/lib/connect.c b/lib/connect.c index fe084e31e0..88eecca344 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -26,7 +26,7 @@ and the Eclipse Distribution License is available at #include "send_mosq.h" #include "socks_mosq.h" -static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking); +static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const struct mqtt5__property *properties); static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); @@ -73,6 +73,11 @@ int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int ke int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) +{ + return mosquitto_connect_bind_with_properties(mosq, host, port, keepalive, bind_address, NULL); +} + +int mosquitto_connect_bind_with_properties(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties) { int rc; rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address); @@ -82,7 +87,7 @@ int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, i mosq->state = mosq_cs_new; pthread_mutex_unlock(&mosq->state_mutex); - return mosquitto__reconnect(mosq, true); + return mosquitto__reconnect(mosq, true, properties); } @@ -101,23 +106,23 @@ int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int p mosq->state = mosq_cs_connect_async; pthread_mutex_unlock(&mosq->state_mutex); - return mosquitto__reconnect(mosq, false); + return mosquitto__reconnect(mosq, false, NULL); } int mosquitto_reconnect_async(struct mosquitto *mosq) { - return mosquitto__reconnect(mosq, false); + return mosquitto__reconnect(mosq, false, NULL); } int mosquitto_reconnect(struct mosquitto *mosq) { - return mosquitto__reconnect(mosq, true); + return mosquitto__reconnect(mosq, true, NULL); } -static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) +static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties) { int rc; struct mosquitto__packet *packet; @@ -193,12 +198,17 @@ static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) }else #endif { - return send__connect(mosq, mosq->keepalive, mosq->clean_session); + return send__connect(mosq, mosq->keepalive, mosq->clean_session, properties); } } int mosquitto_disconnect(struct mosquitto *mosq) +{ + return mosquitto_disconnect_with_properties(mosq, NULL); +} + +int mosquitto_disconnect_with_properties(struct mosquitto *mosq, const mosquitto_property *properties) { if(!mosq) return MOSQ_ERR_INVAL; @@ -207,6 +217,6 @@ int mosquitto_disconnect(struct mosquitto *mosq) pthread_mutex_unlock(&mosq->state_mutex); if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - return send__disconnect(mosq); + return send__disconnect(mosq, properties); } diff --git a/lib/linker.version b/lib/linker.version index 347beeccbe..9ec233cca9 100644 --- a/lib/linker.version +++ b/lib/linker.version @@ -94,8 +94,17 @@ MOSQ_1.5 { MOSQ_1.6 { global: + mosquitto_connect_bind_with_properties; + mosquitto_property_add_binary; + mosquitto_property_add_byte; + mosquitto_property_add_int16; + mosquitto_property_add_int32; + mosquitto_property_add_string; + mosquitto_property_add_string_pair; + mosquitto_property_add_varint; mosquitto_property_free_all; mosquitto_property_command_check; + mosquitto_publish_with_properties; mosquitto_string_to_command; mosquitto_string_to_property_info; mosquitto_subscribe_multiple; diff --git a/lib/mosquitto.h b/lib/mosquitto.h index 338baa8876..c7ba275d86 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -44,6 +44,7 @@ extern "C" { #endif #include +#include #define LIBMOSQUITTO_MAJOR 1 #define LIBMOSQUITTO_MINOR 5 @@ -423,6 +424,37 @@ libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, i */ libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); +/* + * Function: mosquitto_connect_bind_with_properties + * + * Connect to an MQTT broker. This extends the functionality of + * by adding the bind_address parameter. Use this function + * if you need to restrict network communication over a particular interface. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * properties - the MQTT 5 properties for the connect (not for the Will). + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_connect_bind_with_properties(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties); + /* * Function: mosquitto_connect_async * @@ -604,6 +636,26 @@ libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); +/* + * Function: mosquitto_disconnect_with_properties + * + * Disconnect from the broker, with attached MQTT properties. + * + * Use to create a list of properties, then attach + * them to this publish. Properties need freeing with + * . + * + * Parameters: + * mosq - a valid mosquitto instance. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + */ +libmosq_EXPORT int mosquitto_disconnect_with_properties(struct mosquitto *mosq, const mosquitto_property *properties); + /* ====================================================================== * @@ -648,6 +700,58 @@ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain); + +/* + * Function: mosquitto_publish_with_properties + * + * Publish a message on a given topic, with attached MQTT properties. + * + * Use to create a list of properties, then attach + * them to this publish. Properties need freeing with + * . + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - pointer to an int. If not NULL, the function will set this + * to the message id of this particular message. This can be then + * used with the publish callback to determine when the message + * has been sent. + * Note that although the MQTT protocol doesn't use message ids + * for messages with QoS=0, libmosquitto assigns them message ids + * so they can be tracked with this parameter. + * topic - null terminated string of the topic to publish to. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the message. + * retain - set to true to make the message retained. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + */ +libmosq_EXPORT int mosquitto_publish_with_properties( + struct mosquitto *mosq, + int *mid, + const char *topic, + int payloadlen, + const void *payload, + int qos, + bool retain, + const mosquitto_property *properties); + + /* * Function: mosquitto_subscribe * @@ -671,6 +775,37 @@ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const cha */ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos); +/* + * Function: mosquitto_subscribe_with_properties + * + * Subscribe to a topic, with attached MQTT properties. + * + * Use to create a list of properties, then attach + * them to this subscribe. Properties need freeing with + * . + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub - the subscription pattern. + * qos - the requested Quality of Service for this subscription. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + */ +libmosq_EXPORT int mosquitto_subscribe_with_properties(struct mosquitto *mosq, int *mid, const char *sub, int qos, const mosquitto_property *properties); + /* * Function: mosquitto_subscribe_multiple * @@ -689,6 +824,8 @@ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const c * familiar with this, just think of it as a safer "char **", * equivalent to "const char *" for a simple string pointer. * qos - the requested Quality of Service for each subscription. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. * * Returns: * MOSQ_ERR_SUCCESS - on success. @@ -697,7 +834,7 @@ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const c * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 */ -int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos); +int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, const mosquitto_property *properties); /* * Function: mosquitto_unsubscribe @@ -1901,6 +2038,179 @@ libmosq_EXPORT int mosquitto_validate_utf8(const char *str, int len); * ============================================================================= */ + +/* + * Function: mosquitto_property_add_byte + * + * Add a new byte property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value); + +/* + * Function: mosquitto_property_add_int16 + * + * Add a new int16 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_RECEIVE_MAXIMUM) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1000); + */ +libmosq_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value); + +/* + * Function: mosquitto_property_add_int32 + * + * Add a new int32 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400); + */ +libmosq_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_varint + * + * Add a new varint property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_SUBSCRIPTION_IDENTIFIER) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_varint(&proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_binary + * + * Add a new binary property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to the property data + * len - length of property data in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len); + */ +libmosq_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len); + +/* + * Function: mosquitto_property_add_string + * + * Add a new string property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_CONTENT_TYPE) + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - value is not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + */ +libmosq_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value); + +/* + * Function: mosquitto_property_add_string_pair + * + * Add a new string pair property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_USER_PROPERTY) + * name - string name for the new property, must be UTF-8 and zero terminated + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if name or value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - if name or value are not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "client", "mosquitto_pub"); + */ +libmosq_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value); + /* * Function: mosquitto_property_free_all * @@ -1929,7 +2239,7 @@ libmosq_EXPORT void mosquitto_property_free_all(mosquitto_property **properties) * MOSQ_ERR_SUCCESS - if the identifier is valid for command * MOSQ_ERR_PROTOCOL - if the identifier is not valid for use with command. */ -int mosquitto_property_command_check(int command, int identifier); +libmosq_EXPORT int mosquitto_property_command_check(int command, int identifier); /* Function: mosquitto_string_to_property_info * @@ -1951,7 +2261,7 @@ int mosquitto_property_command_check(int command, int identifier); * // id == MQTT_PROP_RESPONSE_TOPIC * // type == MQTT_PROP_TYPE_STRING */ -int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type); +libmosq_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type); #ifdef __cplusplus diff --git a/lib/property_mosq.c b/lib/property_mosq.c index b5556d7dbb..be8ef05e03 100644 --- a/lib/property_mosq.c +++ b/lib/property_mosq.c @@ -290,11 +290,12 @@ void mosquitto_property_free_all(struct mqtt5__property **property) } -int property__get_length(struct mqtt5__property *property) +int property__get_length(const struct mqtt5__property *property) { if(!property) return 0; switch(property->identifier){ + /* Byte */ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: @@ -305,18 +306,21 @@ int property__get_length(struct mqtt5__property *property) case MQTT_PROP_SHARED_SUB_AVAILABLE: return 2; /* 1 (identifier) + 1 byte */ + /* uint16 */ case MQTT_PROP_SERVER_KEEP_ALIVE: case MQTT_PROP_RECEIVE_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS: return 3; /* 1 (identifier) + 2 bytes */ + /* uint32 */ case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: case MQTT_PROP_WILL_DELAY_INTERVAL: case MQTT_PROP_MAXIMUM_PACKET_SIZE: case MQTT_PROP_SESSION_EXPIRY_INTERVAL: - return 5; /* 1 (identifier) + 5 bytes */ + return 5; /* 1 (identifier) + 4 bytes */ + /* varint */ case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: if(property->value.varint < 128){ return 2; @@ -330,10 +334,12 @@ int property__get_length(struct mqtt5__property *property) return 0; } + /* binary */ case MQTT_PROP_CORRELATION_DATA: case MQTT_PROP_AUTHENTICATION_DATA: return 3 + property->value.bin.len; /* 1 + 2 bytes (len) + X bytes (payload) */ + /* string */ case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: @@ -343,6 +349,7 @@ int property__get_length(struct mqtt5__property *property) case MQTT_PROP_REASON_STRING: return 3 + property->value.s.len; /* 1 + 2 bytes (len) + X bytes (string) */ + /* string pair */ case MQTT_PROP_USER_PROPERTY: return 5 + property->value.s.len + property->name.len; /* 1 + 2*(2 bytes (len) + X bytes (string))*/ @@ -353,9 +360,9 @@ int property__get_length(struct mqtt5__property *property) } -int property__get_length_all(struct mqtt5__property *property) +int property__get_length_all(const struct mqtt5__property *property) { - struct mqtt5__property *p; + const struct mqtt5__property *p; int len = 0; p = property; @@ -367,7 +374,7 @@ int property__get_length_all(struct mqtt5__property *property) } -int property__write(struct mosquitto__packet *packet, struct mqtt5__property *property) +int property__write(struct mosquitto__packet *packet, const struct mqtt5__property *property) { int rc; @@ -417,6 +424,7 @@ int property__write(struct mosquitto__packet *packet, struct mqtt5__property *pr case MQTT_PROP_CORRELATION_DATA: packet__write_uint16(packet, property->value.bin.len); packet__write_bytes(packet, property->value.bin.v, property->value.bin.len); + break; case MQTT_PROP_USER_PROPERTY: packet__write_string(packet, property->name.v, property->name.len); @@ -432,10 +440,10 @@ int property__write(struct mosquitto__packet *packet, struct mqtt5__property *pr } -int property__write_all(struct mosquitto__packet *packet, struct mqtt5__property *properties) +int property__write_all(struct mosquitto__packet *packet, const struct mqtt5__property *properties) { int rc; - struct mqtt5__property *p; + const struct mqtt5__property *p; rc = packet__write_varint(packet, property__get_length_all(properties)); if(rc) return rc; @@ -648,3 +656,225 @@ int mosquitto_string_to_property_info(const char *propname, int *identifier, int } return MOSQ_ERR_SUCCESS; } + + +static void property__add(struct mqtt5__property **proplist, struct mqtt5__property *prop) +{ + struct mqtt5__property *p; + + if(!(*proplist)){ + *proplist = prop; + } + + p = *proplist; + while(p->next){ + p = p->next; + } + p->next = prop; + prop->next = NULL; +} + + +int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value) +{ + struct mqtt5__property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR + && identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION + && identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION + && identifier != MQTT_PROP_MAXIMUM_QOS + && identifier != MQTT_PROP_RETAIN_AVAILABLE + && identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE + && identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE + && identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){ + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->identifier = identifier; + prop->value.i8 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value) +{ + struct mqtt5__property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_SERVER_KEEP_ALIVE + && identifier != MQTT_PROP_RECEIVE_MAXIMUM + && identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM + && identifier != MQTT_PROP_TOPIC_ALIAS){ + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->identifier = identifier; + prop->value.i16 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value) +{ + struct mqtt5__property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL + && identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL + && identifier != MQTT_PROP_WILL_DELAY_INTERVAL + && identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->identifier = identifier; + prop->value.i32 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value) +{ + struct mqtt5__property *prop; + + if(!proplist || value > 268435455) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER) return MOSQ_ERR_INVAL; + + prop = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->identifier = identifier; + prop->value.varint = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len) +{ + struct mqtt5__property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_CORRELATION_DATA + && identifier != MQTT_PROP_AUTHENTICATION_DATA){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->identifier = identifier; + + if(len){ + prop->value.bin.v = mosquitto__malloc(len); + if(!prop->value.bin.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + + memcpy(prop->value.bin.v, value, len); + prop->value.bin.len = len; + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value) +{ + struct mqtt5__property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(value){ + if(mosquitto_validate_utf8(value, strlen(value))) return MOSQ_ERR_MALFORMED_UTF8; + } + + if(identifier != MQTT_PROP_CONTENT_TYPE + && identifier != MQTT_PROP_RESPONSE_TOPIC + && identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER + && identifier != MQTT_PROP_AUTHENTICATION_METHOD + && identifier != MQTT_PROP_RESPONSE_INFORMATION + && identifier != MQTT_PROP_SERVER_REFERENCE + && identifier != MQTT_PROP_REASON_STRING){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->identifier = identifier; + if(value && strlen(value)){ + prop->value.s.v = mosquitto__strdup(value); + if(!prop->value.s.v){ + mosquitto__free(prop->name.v); + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->value.s.len = strlen(value); + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value) +{ + struct mqtt5__property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_USER_PROPERTY) return MOSQ_ERR_INVAL; + if(name){ + if(mosquitto_validate_utf8(name, strlen(name))) return MOSQ_ERR_MALFORMED_UTF8; + } + if(value){ + if(mosquitto_validate_utf8(value, strlen(value))) return MOSQ_ERR_MALFORMED_UTF8; + } + + prop = mosquitto__calloc(1, sizeof(struct mqtt5__property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->identifier = identifier; + + if(name && strlen(name)){ + prop->name.v = mosquitto__strdup(name); + if(!prop->name.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->name.len = strlen(name); + } + + if(value && strlen(value)){ + prop->value.s.v = mosquitto__strdup(value); + if(!prop->value.s.v){ + mosquitto__free(prop->name.v); + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->value.s.len = strlen(value); + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} diff --git a/lib/property_mosq.h b/lib/property_mosq.h index 5fbd311f2f..dd2ecf9578 100644 --- a/lib/property_mosq.h +++ b/lib/property_mosq.h @@ -40,10 +40,10 @@ struct mqtt5__property { int property__read_all(int command, struct mosquitto__packet *packet, struct mqtt5__property **property); -int property__write_all(struct mosquitto__packet *packet, struct mqtt5__property *property); +int property__write_all(struct mosquitto__packet *packet, const struct mqtt5__property *property); void property__free(struct mqtt5__property **property); -int property__get_length(struct mqtt5__property *property); -int property__get_length_all(struct mqtt5__property *property); +int property__get_length(const struct mqtt5__property *property); +int property__get_length_all(const struct mqtt5__property *property); #endif diff --git a/lib/send_connect.c b/lib/send_connect.c index f8859ef056..1c703fd72b 100644 --- a/lib/send_connect.c +++ b/lib/send_connect.c @@ -31,7 +31,7 @@ and the Eclipse Distribution License is available at #include "packet_mosq.h" #include "property_mosq.h" -int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session) +int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const struct mqtt5__property *properties) { struct mosquitto__packet *packet = NULL; int payloadlen; @@ -41,7 +41,6 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session uint8_t version; char *clientid, *username, *password; int headerlen; - struct mqtt5__property *properties = NULL; int proplen, varbytes; assert(mosq); diff --git a/lib/send_disconnect.c b/lib/send_disconnect.c index 1258f5c08c..9593ea2eb8 100644 --- a/lib/send_disconnect.c +++ b/lib/send_disconnect.c @@ -32,11 +32,10 @@ and the Eclipse Distribution License is available at #include "send_mosq.h" -int send__disconnect(struct mosquitto *mosq) +int send__disconnect(struct mosquitto *mosq, const struct mqtt5__property *properties) { struct mosquitto__packet *packet = NULL; int rc; - struct mqtt5__property *properties = NULL; int proplen, varbytes; assert(mosq); diff --git a/lib/send_mosq.c b/lib/send_mosq.c index 9bf441a1e4..eb0d6561b9 100644 --- a/lib/send_mosq.c +++ b/lib/send_mosq.c @@ -111,7 +111,7 @@ int send__pubrel(struct mosquitto *mosq, uint16_t mid) } /* For PUBACK, PUBCOMP, PUBREC, and PUBREL */ -int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, struct mqtt5__property *properties) +int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const struct mqtt5__property *properties) { struct mosquitto__packet *packet = NULL; int rc; diff --git a/lib/send_mosq.h b/lib/send_mosq.h index 34b1b8788b..68d118995c 100644 --- a/lib/send_mosq.h +++ b/lib/send_mosq.h @@ -20,19 +20,19 @@ and the Eclipse Distribution License is available at #include "property_mosq.h" int send__simple_command(struct mosquitto *mosq, uint8_t command); -int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, struct mqtt5__property *properties); -int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, struct mqtt5__property *properties); +int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const struct mqtt5__property *properties); +int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const struct mqtt5__property *properties); -int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session); -int send__disconnect(struct mosquitto *mosq); +int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const struct mqtt5__property *properties); +int send__disconnect(struct mosquitto *mosq, const struct mqtt5__property *properties); int send__pingreq(struct mosquitto *mosq); int send__pingresp(struct mosquitto *mosq); int send__puback(struct mosquitto *mosq, uint16_t mid); int send__pubcomp(struct mosquitto *mosq, uint16_t mid); -int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, struct mqtt5__property *properties); +int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const struct mqtt5__property *properties); int send__pubrec(struct mosquitto *mosq, uint16_t mid); int send__pubrel(struct mosquitto *mosq, uint16_t mid); -int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos); +int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const struct mqtt5__property *properties); int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic); #endif diff --git a/lib/send_publish.c b/lib/send_publish.c index 9ac9d8d238..ef71ab7199 100644 --- a/lib/send_publish.c +++ b/lib/send_publish.c @@ -37,7 +37,7 @@ and the Eclipse Distribution License is available at #include "send_mosq.h" -int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, struct mqtt5__property *properties) +int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const struct mqtt5__property *properties) { #ifdef WITH_BROKER size_t len; @@ -129,7 +129,7 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 } -int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, struct mqtt5__property *properties) +int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const struct mqtt5__property *properties) { struct mosquitto__packet *packet = NULL; int packetlen; diff --git a/lib/send_subscribe.c b/lib/send_subscribe.c index 498919ff77..7feeb3a9f7 100644 --- a/lib/send_subscribe.c +++ b/lib/send_subscribe.c @@ -33,14 +33,13 @@ and the Eclipse Distribution License is available at #include "util_mosq.h" -int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const char **topic, int topic_qos) +int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const char **topic, int topic_qos, const struct mqtt5__property *properties) { struct mosquitto__packet *packet = NULL; uint32_t packetlen; uint16_t local_mid; int rc; int i; - struct mqtt5__property *properties = NULL; int proplen, varbytes; assert(mosq); diff --git a/lib/socks_mosq.c b/lib/socks_mosq.c index f8f006a401..a3cfc015d3 100644 --- a/lib/socks_mosq.c +++ b/lib/socks_mosq.c @@ -417,7 +417,7 @@ int socks5__read(struct mosquitto *mosq) /* Auth passed */ packet__cleanup(&mosq->in_packet); mosq->state = mosq_cs_new; - return send__connect(mosq, mosq->keepalive, mosq->clean_session); + return send__connect(mosq, mosq->keepalive, mosq->clean_session, NULL); }else{ i = mosq->in_packet.payload[1]; packet__cleanup(&mosq->in_packet); diff --git a/man/mosquitto_pub.1.xml b/man/mosquitto_pub.1.xml index 0d6fc31482..e3b43df804 100644 --- a/man/mosquitto_pub.1.xml +++ b/man/mosquitto_pub.1.xml @@ -46,6 +46,7 @@ + command identifier value topic payload @@ -73,6 +74,7 @@ socks-url protocol-version + command identifier value @@ -313,6 +315,39 @@ option. + + + + + Use an MQTT v5 property with this publish. If you use + this option, the client will be set to be an MQTT v5 + client. This option has two forms: + + + is the MQTT command/packet + identifier and can be one of CONNECT, PUBLISH, PUBREL, + DISCONNECT, AUTH, or WILL. The properties available for + each command are listed in the Properties + section. + + is the name of the + property to add. This is as described in the + specification, but with '-' as a word separator. For + example: + . More details + are in the Properties section. + + is the value of the property + to add, with a data type that is property + specific. + + is only used for the + property as the first of + the two strings in the string pair. In that case, + is the second of the strings in + the pair. + + @@ -481,6 +516,62 @@ arguments to modify the other will parameters. + + Properties + The option allows adding properties + to different stages of the mosquitto_pub run. The properties + supported for each command are as follows: + + + Connect + + (binary data - note treated as a string in mosquitto_pub) + (UTF-8 string pair) + (32-bit unsigned integer) + (16-bit unsigned integer) + (8-bit unsigned integer) + (8-bit unsigned integer) + (32-bit unsigned integer) + (16-bit unsigned integer) + (UTF-8 string pair) + + + + + Publish + + (UTF-8 string) + (binary data - note treated as a string in mosquitto_pub) + (32-bit unsigned integer) + (8-bit unsigned integer) + (UTF-8 string) + (16-bit unsigned integer) + (UTF-8 string pair) + + + + + Disconnect + + (32-bit unsigned integer) + (UTF-8 string pair) + + + + + Will properties + + (UTF-8 string) + (binary data - note treated as a string in mosquitto_pub) + (32-bit unsigned integer) + (8-bit unsigned integer) + (UTF-8 string) + (UTF-8 string pair) + (32-bit unsigned integer) + + + + Examples Publish temperature information to localhost with QoS 1: diff --git a/man/mosquitto_sub.1.xml b/man/mosquitto_sub.1.xml index 44e93cb1d6..696c6c51d6 100644 --- a/man/mosquitto_sub.1.xml +++ b/man/mosquitto_sub.1.xml @@ -52,6 +52,7 @@ protocol-version message processing timeout + command identifier value socks-url @@ -77,6 +78,7 @@ version + command identifier value mosquitto_sub @@ -343,6 +345,39 @@ option. + + + + + Use an MQTT v5 property with this publish. If you use + this option, the client will be set to be an MQTT v5 + client. This option has two forms: + + + is the MQTT command/packet + identifier and can be one of CONNECT, PUBACK, PUBREC, + PUBCOMP, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, AUTH, or + WILL. The properties available for each command are + listed in the Properties section. + + is the name of the + property to add. This is as described in the + specification, but with '-' as a word separator. For + example: + . More details + are in the Properties section. + + is the value of the property + to add, with a data type that is property + specific. + + is only used for the + property as the first of + the two strings in the string pair. In that case, + is the second of the strings in + the pair. + + @@ -703,6 +738,63 @@ modify the other will parameters. + + Properties + The option allows adding properties + to different stages of the mosquitto_sub run. The properties + supported for each command are as follows: + + + Connect + + (binary data - note treated as a string in mosquitto_pub) + (UTF-8 string pair) + (32-bit unsigned integer) + (16-bit unsigned integer) + (8-bit unsigned integer) + (8-bit unsigned integer) + (32-bit unsigned integer) + (16-bit unsigned integer) + (UTF-8 string pair) + + + + + Subscribe + + (UTF-8 string pair) + + + + + Unsubscribe + + (UTF-8 string pair) + + + + + Disconnect + + (32-bit unsigned integer) + (UTF-8 string pair) + + + + + Will properties + + (UTF-8 string) + (binary data - note treated as a string in mosquitto_pub) + (32-bit unsigned integer) + (8-bit unsigned integer) + (UTF-8 string) + (UTF-8 string pair) + (32-bit unsigned integer) + + + + Examples Note that these really are examples - the subscriptions will work diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97f1d3b4e6..3d93b2c5c6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,7 @@ set (MOSQ_SRCS send_suback.c signals.c ../lib/send_subscribe.c + send_unsuback.c ../lib/send_unsubscribe.c sys_tree.c sys_tree.h ../lib/time_mosq.c diff --git a/src/Makefile b/src/Makefile index 93b2ea4a8f..dcb92da4f3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -46,6 +46,7 @@ OBJS= mosquitto.o \ send_publish.o \ send_suback.o \ send_subscribe.o \ + send_unsuback.o \ send_unsubscribe.o \ service.o \ signals.o \ @@ -176,6 +177,9 @@ send_suback.o : send_suback.c mosquitto_broker_internal.h send_subscribe.o : ../lib/send_subscribe.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +send_unsuback.o : send_unsuback.c mosquitto_broker_internal.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + send_unsubscribe.o : ../lib/send_unsubscribe.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ diff --git a/src/bridge.c b/src/bridge.c index 77ccf34123..cbff3a9a54 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -256,7 +256,7 @@ int bridge__connect_step3(struct mosquitto_db *db, struct mosquitto *context) context->bridge->primary_retry = mosquitto_time() + 5; } - rc = send__connect(context, context->keepalive, context->clean_session); + rc = send__connect(context, context->keepalive, context->clean_session, NULL); if(rc == MOSQ_ERR_SUCCESS){ return MOSQ_ERR_SUCCESS; }else if(rc == MOSQ_ERR_ERRNO && errno == ENOTCONN){ @@ -375,7 +375,7 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) HASH_ADD(hh_sock, db->contexts_by_sock, sock, sizeof(context->sock), context); - rc2 = send__connect(context, context->keepalive, context->clean_session); + rc2 = send__connect(context, context->keepalive, context->clean_session, NULL); if(rc2 == MOSQ_ERR_SUCCESS){ return rc; }else if(rc2 == MOSQ_ERR_ERRNO && errno == ENOTCONN){ diff --git a/src/handle_connack.c b/src/handle_connack.c index ca3321697a..c04625ae6d 100644 --- a/src/handle_connack.c +++ b/src/handle_connack.c @@ -86,7 +86,7 @@ int handle__connack(struct mosquitto_db *db, struct mosquitto *context) } for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_in || context->bridge->topics[i].direction == bd_both){ - if(send__subscribe(context, NULL, 1, &context->bridge->topics[i].remote_topic, context->bridge->topics[i].qos)){ + if(send__subscribe(context, NULL, 1, &context->bridge->topics[i].remote_topic, context->bridge->topics[i].qos, NULL)){ return 1; } }else{ diff --git a/src/handle_unsubscribe.c b/src/handle_unsubscribe.c index 296fef1194..d760a5d7c7 100644 --- a/src/handle_unsubscribe.c +++ b/src/handle_unsubscribe.c @@ -92,6 +92,6 @@ int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context) log__printf(NULL, MOSQ_LOG_DEBUG, "Sending UNSUBACK to %s", context->id); /* We don't use Reason String or User Property yet. */ - return send__command_with_mid(context, CMD_UNSUBACK, mid, false, NULL); + return send__unsuback(context, mid, NULL); } diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index f9970a78f2..7a199b11f9 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -512,6 +512,7 @@ int restore_privileges(void); * ============================================================ */ int send__connack(struct mosquitto *context, int ack, int result); int send__suback(struct mosquitto *context, uint16_t mid, uint32_t payloadlen, const void *payload); +int send__unsuback(struct mosquitto *context, uint16_t mid, const struct mqtt5__property *properties); /* ============================================================ * Network functions diff --git a/src/send_unsuback.c b/src/send_unsuback.c new file mode 100644 index 0000000000..e48b6547fb --- /dev/null +++ b/src/send_unsuback.c @@ -0,0 +1,60 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include + +#include "mosquitto_broker_internal.h" +#include "mqtt_protocol.h" +#include "memory_mosq.h" +#include "packet_mosq.h" +#include "property_mosq.h" + + +int send__unsuback(struct mosquitto *mosq, uint16_t mid, const struct mqtt5__property *properties) +{ + struct mosquitto__packet *packet = NULL; + int rc; + int proplen, varbytes; + + assert(mosq); + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packet->command = CMD_UNSUBACK; + packet->remaining_length = 2; + + if(mosq->protocol == mosq_p_mqtt5){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packet->remaining_length += varbytes + proplen; + } + + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + + packet__write_uint16(packet, mid); + + if(mosq->protocol == mosq_p_mqtt5){ + property__write_all(packet, properties); + } + + return packet__queue(mosq, packet); +} diff --git a/test/mosq_test.py b/test/mosq_test.py index 18cdec8743..85447d06a6 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -371,6 +371,9 @@ def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0, proto_ pack_format = pack_format + "H" if proto_ver == 5: + if properties is None: + properties = struct.pack("!B", 0) + rl += len(properties) # This will break if len(properties) > 127 pack_format = pack_format + "%ds"%(len(properties)) @@ -400,33 +403,27 @@ def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0, proto_ else: return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, payload) -def gen_puback(mid, proto_ver=4): +def _gen_command_with_mid(cmd, mid, proto_ver=4, reason_code=0): if proto_ver == 5: - return struct.pack('!BBHB', 64, 3, mid, 0) + return struct.pack('!BBHBB', cmd, 4, mid, reason_code, 0) else: - return struct.pack('!BBH', 64, 2, mid) + return struct.pack('!BBH', cmd, 2, mid) -def gen_pubrec(mid, proto_ver=4): - if proto_ver == 5: - return struct.pack('!BBHB', 80, 3, mid, 0) - else: - return struct.pack('!BBH', 80, 2, mid) +def gen_puback(mid, proto_ver=4, reason_code=0): + return _gen_command_with_mid(64, mid, proto_ver, reason_code) + +def gen_pubrec(mid, proto_ver=4, reason_code=0): + return _gen_command_with_mid(80, mid, proto_ver, reason_code) -def gen_pubrel(mid, dup=False, proto_ver=4): +def gen_pubrel(mid, dup=False, proto_ver=4, reason_code=0): if dup: cmd = 96+8+2 else: cmd = 96+2 - if proto_ver == 5: - return struct.pack('!BBHB', cmd, 3, mid, 0) - else: - return struct.pack('!BBH', cmd, 2, mid) + return _gen_command_with_mid(cmd, mid, proto_ver, reason_code) -def gen_pubcomp(mid, proto_ver=4): - if proto_ver == 5: - return struct.pack('!BBHB', 112, 3, mid, 0) - else: - return struct.pack('!BBH', 112, 2, mid) +def gen_pubcomp(mid, proto_ver=4, reason_code=0): + return _gen_command_with_mid(112, mid, proto_ver, reason_code) def gen_subscribe(mid, topic, qos, proto_ver=4): if proto_ver == 5: