Exemple #1
0
int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub)
{
	if(!mosq) return MOSQ_ERR_INVAL;
	if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;

	if(mosquitto_sub_topic_check(sub)) return MOSQ_ERR_INVAL;

	return _mosquitto_send_unsubscribe(mosq, mid, sub);
}
int mqtt3_handle_unsubscribe(struct mosquitto_db *db, struct mosquitto *context)
{
	uint16_t mid;
	char *sub;

	if(!context) return MOSQ_ERR_INVAL;
	_mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBSCRIBE from %s", context->id);

	if(context->protocol == mosq_p_mqtt311){
		if((context->in_packet.command&0x0F) != 0x02){
			return MOSQ_ERR_PROTOCOL;
		}
	}
	if(_mosquitto_read_uint16(&context->in_packet, &mid)) return 1;
    g_epoll_unsubscribe_num++;
	while(context->in_packet.pos < context->in_packet.remaining_length){
		sub = NULL;
		if(_mosquitto_read_string(&context->in_packet, &sub)){
			return 1;
		}

		if(sub){
			if(!strlen(sub)){
				_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Empty unsubscription string from %s, disconnecting.",
					context->id);
				_mosquitto_free(sub);
				return 1;
			}
			if(mosquitto_sub_topic_check(sub)){
				_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Invalid unsubscription string from %s, disconnecting.",
					context->id);
				_mosquitto_free(sub);
				return 1;
			}

			_mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub);
			mqtt3_sub_remove(db, context, sub, &db->subs);
			_mosquitto_log_printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub);
			_mosquitto_free(sub);
		}
	}
#ifdef WITH_PERSISTENCE
	db->persistence_changes++;
#endif

	return _mosquitto_send_command_with_mid(context, UNSUBACK, mid, false);
}
Exemple #3
0
/* Process a tokenised single line from a file or set of real argc/argv */
int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
    int i;

    for(i=1; i<argc; i++) {
        if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
                return 1;
            } else {
                cfg->port = atoi(argv[i+1]);
                if(cfg->port<1 || cfg->port>65535) {
                    fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
                    return 1;
                }
            }
            i++;
        } else if(!strcmp(argv[i], "-A")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -A argument given but no address specified.\n\n");
                return 1;
            } else {
                cfg->bind_address = strdup(argv[i+1]);
            }
            i++;
#ifdef WITH_TLS
        } else if(!strcmp(argv[i], "--cafile")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
                return 1;
            } else {
                cfg->cafile = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "--capath")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
                return 1;
            } else {
                cfg->capath = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "--cert")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
                return 1;
            } else {
                cfg->certfile = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "--ciphers")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n");
                return 1;
            } else {
                cfg->ciphers = strdup(argv[i+1]);
            }
            i++;
#endif
        } else if(!strcmp(argv[i], "-C")) {
            if(pub_or_sub == CLIENT_PUB) {
                goto unknown_option;
            } else {
                if(i==argc-1) {
                    fprintf(stderr, "Error: -C argument given but no count specified.\n\n");
                    return 1;
                } else {
                    cfg->msg_count = atoi(argv[i+1]);
                    if(cfg->msg_count < 1) {
                        fprintf(stderr, "Error: Invalid message count \"%d\".\n\n", cfg->msg_count);
                        return 1;
                    }
                }
                i++;
            }
        } else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
            cfg->debug = true;
        } else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")) {
            if(pub_or_sub == CLIENT_SUB) {
                goto unknown_option;
            }
            if(cfg->pub_mode != MSGMODE_NONE) {
                fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
                return 1;
            } else if(i==argc-1) {
                fprintf(stderr, "Error: -f argument given but no file specified.\n\n");
                return 1;
            } else {
                cfg->pub_mode = MSGMODE_FILE;
                cfg->file_input = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "--help")) {
            return 2;
        } else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
                return 1;
            } else {
                cfg->host = strdup(argv[i+1]);
            }
            i++;
#ifdef WITH_TLS
        } else if(!strcmp(argv[i], "--insecure")) {
            cfg->insecure = true;
#endif
        } else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")) {
            if(cfg->id_prefix) {
                fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
                return 1;
            }
            if(i==argc-1) {
                fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
                return 1;
            } else {
                cfg->id = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")) {
            if(cfg->id) {
                fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
                return 1;
            }
            if(i==argc-1) {
                fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n");
                return 1;
            } else {
                cfg->id_prefix = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n");
                return 1;
            } else {
                cfg->keepalive = atoi(argv[i+1]);
                if(cfg->keepalive>65535) {
                    fprintf(stderr, "Error: Invalid keepalive given: %d\n", cfg->keepalive);
                    return 1;
                }
            }
            i++;
#ifdef WITH_TLS
        } else if(!strcmp(argv[i], "--key")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
                return 1;
            } else {
                cfg->keyfile = strdup(argv[i+1]);
            }
            i++;
#endif
        } else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--stdin-line")) {
            if(pub_or_sub == CLIENT_SUB) {
                goto unknown_option;
            }
            if(cfg->pub_mode != MSGMODE_NONE) {
                fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
                return 1;
            } else {
                cfg->pub_mode = MSGMODE_STDIN_LINE;
            }
        } else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--message")) {
            if(pub_or_sub == CLIENT_SUB) {
                goto unknown_option;
            }
            if(cfg->pub_mode != MSGMODE_NONE) {
                fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
                return 1;
            } else if(i==argc-1) {
                fprintf(stderr, "Error: -m argument given but no message specified.\n\n");
                return 1;
            } else {
                cfg->message = strdup(argv[i+1]);
                cfg->msglen = strlen(cfg->message);
                cfg->pub_mode = MSGMODE_CMD;
            }
            i++;
        } else if(!strcmp(argv[i], "-M")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -M argument given but max_inflight not specified.\n\n");
                return 1;
            } else {
                cfg->max_inflight = atoi(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")) {
            if(pub_or_sub == CLIENT_SUB) {
                goto unknown_option;
            }
            if(cfg->pub_mode != MSGMODE_NONE) {
                fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
                return 1;
            } else {
                cfg->pub_mode = MSGMODE_NULL;
            }
        } else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
                return 1;
            } else {
                if(!strcmp(argv[i+1], "mqttv31")) {
                    cfg->protocol_version = MQTT_PROTOCOL_V31;
                } else if(!strcmp(argv[i+1], "mqttv311")) {
                    cfg->protocol_version = MQTT_PROTOCOL_V311;
                } else {
                    fprintf(stderr, "Error: Invalid protocol version argument given.\n\n");
                    return 1;
                }
                i++;
            }
#ifdef WITH_SOCKS
        } else if(!strcmp(argv[i], "--proxy")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
                return 1;
            } else {
                if(mosquitto__parse_socks_url(cfg, argv[i+1])) {
                    return 1;
                }
                i++;
            }
#endif
#ifdef WITH_TLS_PSK
        } else if(!strcmp(argv[i], "--psk")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
                return 1;
            } else {
                cfg->psk = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "--psk-identity")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
                return 1;
            } else {
                cfg->psk_identity = strdup(argv[i+1]);
            }
            i++;
#endif
        } else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
                return 1;
            } else {
                cfg->qos = atoi(argv[i+1]);
                if(cfg->qos<0 || cfg->qos>2) {
                    fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos);
                    return 1;
                }
            }
            i++;
        } else if(!strcmp(argv[i], "--quiet")) {
            cfg->quiet = true;
        } else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--retain")) {
            if(pub_or_sub == CLIENT_SUB) {
                goto unknown_option;
            }
            cfg->retain = 1;
        } else if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stdin-file")) {
            if(pub_or_sub == CLIENT_SUB) {
                goto unknown_option;
            }
            if(cfg->pub_mode != MSGMODE_NONE) {
                fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
                return 1;
            } else {
                cfg->pub_mode = MSGMODE_STDIN_FILE;
            }
#ifdef WITH_SRV
        } else if(!strcmp(argv[i], "-S")) {
            cfg->use_srv = true;
#endif
        } else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -t argument given but no topic specified.\n\n");
                return 1;
            } else {
                if(pub_or_sub == CLIENT_PUB) {
                    if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) {
                        fprintf(stderr, "Error: Invalid publish topic '%s', does it contain '+' or '#'?\n", argv[i+1]);
                        return 1;
                    }
                    cfg->topic = strdup(argv[i+1]);
                } else {
                    if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) {
                        fprintf(stderr, "Error: Invalid subscription topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
                        return 1;
                    }
                    cfg->topic_count++;
                    cfg->topics = realloc(cfg->topics, cfg->topic_count*sizeof(char *));
                    cfg->topics[cfg->topic_count-1] = strdup(argv[i+1]);
                }
                i++;
            }
        } else if(!strcmp(argv[i], "-T") || !strcmp(argv[i], "--filter-out")) {
            if(pub_or_sub == CLIENT_PUB) {
                goto unknown_option;
            }
            if(i==argc-1) {
                fprintf(stderr, "Error: -T argument given but no topic filter specified.\n\n");
                return 1;
            } else {
                if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) {
                    fprintf(stderr, "Error: Invalid filter topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
                    return 1;
                }
                cfg->filter_out_count++;
                cfg->filter_outs = realloc(cfg->filter_outs, cfg->filter_out_count*sizeof(char *));
                cfg->filter_outs[cfg->filter_out_count-1] = strdup(argv[i+1]);
            }
            i++;
#ifdef WITH_TLS
        } else if(!strcmp(argv[i], "--tls-version")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
                return 1;
            } else {
                cfg->tls_version = strdup(argv[i+1]);
            }
            i++;
#endif
        } else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
                return 1;
            } else {
                cfg->username = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
                return 1;
            } else {
                cfg->password = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "--will-payload")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n");
                return 1;
            } else {
                cfg->will_payload = strdup(argv[i+1]);
                cfg->will_payloadlen = strlen(cfg->will_payload);
            }
            i++;
        } else if(!strcmp(argv[i], "--will-qos")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n");
                return 1;
            } else {
                cfg->will_qos = atoi(argv[i+1]);
                if(cfg->will_qos < 0 || cfg->will_qos > 2) {
                    fprintf(stderr, "Error: Invalid will QoS %d.\n\n", cfg->will_qos);
                    return 1;
                }
            }
            i++;
        } else if(!strcmp(argv[i], "--will-retain")) {
            cfg->will_retain = true;
        } else if(!strcmp(argv[i], "--will-topic")) {
            if(i==argc-1) {
                fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
                return 1;
            } else {
                if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) {
                    fprintf(stderr, "Error: Invalid will topic '%s', does it contain '+' or '#'?\n", argv[i+1]);
                    return 1;
                }
                cfg->will_topic = strdup(argv[i+1]);
            }
            i++;
        } else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")) {
            if(pub_or_sub == CLIENT_PUB) {
                goto unknown_option;
            }
            cfg->clean_session = false;
        } else if(!strcmp(argv[i], "-N")) {
            if(pub_or_sub == CLIENT_PUB) {
                goto unknown_option;
            }
            cfg->eol = false;
        } else if(!strcmp(argv[i], "-R")) {
            if(pub_or_sub == CLIENT_PUB) {
                goto unknown_option;
            }
            cfg->no_retain = true;
        } else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) {
            if(pub_or_sub == CLIENT_PUB) {
                goto unknown_option;
            }
            cfg->verbose = 1;
        } else {
            goto unknown_option;
        }
    }

    return MOSQ_ERR_SUCCESS;

unknown_option:
    fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
    return 1;
}
int mqtt3_handle_subscribe(struct mosquitto_db *db, struct mosquitto *context)
{
	int rc = 0;
	int rc2;
	uint16_t mid;
	char *sub;
	uint8_t qos;
	uint8_t *payload = NULL, *tmp_payload;
	uint32_t payloadlen = 0;
	int len;
	char *sub_mount;

	if(!context) return MOSQ_ERR_INVAL;
	_mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received SUBSCRIBE from %s", context->id);
	/* FIXME - plenty of potential for memory leaks here */

	if(context->protocol == mosq_p_mqtt311){
		if((context->in_packet.command&0x0F) != 0x02){
			return MOSQ_ERR_PROTOCOL;
		}
	}
	if(_mosquitto_read_uint16(&context->in_packet, &mid)) return 1;
    g_epoll_subscribe_num++;
	while(context->in_packet.pos < context->in_packet.remaining_length){
		sub = NULL;
		if(_mosquitto_read_string(&context->in_packet, &sub)){
			if(payload) _mosquitto_free(payload);
			return 1;
		}

		if(sub){
			if(!strlen(sub)){
				_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Empty subscription string from %s, disconnecting.",
					context->address);
				_mosquitto_free(sub);
				if(payload) _mosquitto_free(payload);
				return 1;
			}
			if(mosquitto_sub_topic_check(sub)){
				_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Invalid subscription string from %s, disconnecting.",
					context->address);
				_mosquitto_free(sub);
				if(payload) _mosquitto_free(payload);
				return 1;
			}

			if(_mosquitto_read_byte(&context->in_packet, &qos)){
				_mosquitto_free(sub);
				if(payload) _mosquitto_free(payload);
				return 1;
			}
			if(qos > 2){
				_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Invalid QoS in subscription command from %s, disconnecting.",
					context->address);
				_mosquitto_free(sub);
				if(payload) _mosquitto_free(payload);
				return 1;
			}
			if(context->listener && context->listener->mount_point){
				len = strlen(context->listener->mount_point) + strlen(sub) + 1;
				sub_mount = _mosquitto_malloc(len+1);
				if(!sub_mount){
					_mosquitto_free(sub);
					if(payload) _mosquitto_free(payload);
					return MOSQ_ERR_NOMEM;
				}
				snprintf(sub_mount, len, "%s%s", context->listener->mount_point, sub);
				sub_mount[len] = '\0';

				_mosquitto_free(sub);
				sub = sub_mount;

			}
			_mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "\t%s (QoS %d)", sub, qos);

#if 0
			/* FIXME
			 * This section has been disabled temporarily. mosquitto_acl_check
			 * calls mosquitto_topic_matches_sub, which can't cope with
			 * checking subscriptions that have wildcards against ACLs that
			 * have wildcards. Bug #1374291 is related.
			 *
			 * It's a very difficult problem when an ACL looks like foo/+/bar
			 * and a subscription request to foo/# is made.
			 *
			 * This should be changed to using MOSQ_ACL_SUBSCRIPTION in the
			 * future anyway.
			 */
			if(context->protocol == mosq_p_mqtt311){
				rc = mosquitto_acl_check(db, context, sub, MOSQ_ACL_READ);
				switch(rc){
					case MOSQ_ERR_SUCCESS:
						break;
					case MOSQ_ERR_ACL_DENIED:
						qos = 0x80;
						break;
					default:
						_mosquitto_free(sub);
						return rc;
				}
			}
#endif

			if(qos != 0x80){
				rc2 = mqtt3_sub_add(db, context, sub, qos, &db->subs);
				if(rc2 == MOSQ_ERR_SUCCESS){
					if(mqtt3_retain_queue(db, context, sub, qos)) rc = 1;
				}else if(rc2 != -1){
					rc = rc2;
				}
				_mosquitto_log_printf(NULL, MOSQ_LOG_SUBSCRIBE, "%s %d %s", context->id, qos, sub);
			}
			_mosquitto_free(sub);

			tmp_payload = _mosquitto_realloc(payload, payloadlen + 1);
			if(tmp_payload){
				payload = tmp_payload;
				payload[payloadlen] = qos;
				payloadlen++;
			}else{
				if(payload) _mosquitto_free(payload);

				return MOSQ_ERR_NOMEM;
			}
		}
	}

	if(context->protocol == mosq_p_mqtt311){
		if(payloadlen == 0){
			/* No subscriptions specified, protocol error. */
			return MOSQ_ERR_PROTOCOL;
		}
	}
	if(_mosquitto_send_suback(context, mid, payloadlen, payload)) rc = 1;
	_mosquitto_free(payload);
	
#ifdef WITH_PERSISTENCE
	db->persistence_changes++;
#endif

	return rc;
}
int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context)
{
	uint16_t mid;
	char *sub;
	int slen;
	int rc;
	uint8_t reason;
	int reason_code_count = 0;
	int reason_code_max;
	uint8_t *reason_codes = NULL, *reason_tmp;
	mosquitto_property *properties = NULL;

	if(!context) return MOSQ_ERR_INVAL;

	if(context->state != mosq_cs_connected){
		return MOSQ_ERR_PROTOCOL;
	}
	log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBSCRIBE from %s", context->id);

	if(context->protocol != mosq_p_mqtt31){
		if((context->in_packet.command&0x0F) != 0x02){
			return MOSQ_ERR_PROTOCOL;
		}
	}
	if(packet__read_uint16(&context->in_packet, &mid)) return 1;
	if(mid == 0) return MOSQ_ERR_PROTOCOL;

	if(context->protocol == mosq_p_mqtt5){
		rc = property__read_all(CMD_UNSUBSCRIBE, &context->in_packet, &properties);
		if(rc) return rc;
		/* Immediately free, we don't do anything with User Property at the moment */
		mosquitto_property_free_all(&properties);
	}

	if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){
		if(context->in_packet.pos == context->in_packet.remaining_length){
			/* No topic specified, protocol error. */
			return MOSQ_ERR_PROTOCOL;
		}
	}

	reason_code_max = 10;
	reason_codes = mosquitto__malloc(reason_code_max);
	if(!reason_codes){
		return MOSQ_ERR_NOMEM;
	}

	while(context->in_packet.pos < context->in_packet.remaining_length){
		sub = NULL;
		if(packet__read_string(&context->in_packet, &sub, &slen)){
			return 1;
		}

		if(!slen){
			log__printf(NULL, MOSQ_LOG_INFO,
					"Empty unsubscription string from %s, disconnecting.",
					context->id);
			mosquitto__free(sub);
			return 1;
		}
		if(mosquitto_sub_topic_check(sub)){
			log__printf(NULL, MOSQ_LOG_INFO,
					"Invalid unsubscription string from %s, disconnecting.",
					context->id);
			mosquitto__free(sub);
			return 1;
		}

		log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub);
		rc = sub__remove(db, context, sub, db->subs, &reason);
		log__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub);
		mosquitto__free(sub);
		if(rc) return rc;

		reason_codes[reason_code_count] = reason;
		reason_code_count++;
		if(reason_code_count == reason_code_max){
			reason_tmp = mosquitto__realloc(reason_codes, reason_code_max*2);
			if(!reason_tmp){
				mosquitto__free(reason_codes);
				return MOSQ_ERR_NOMEM;
			}
			reason_codes = reason_tmp;
			reason_code_max *= 2;
		}
	}
#ifdef WITH_PERSISTENCE
	db->persistence_changes++;
#endif

	log__printf(NULL, MOSQ_LOG_DEBUG, "Sending UNSUBACK to %s", context->id);

	/* We don't use Reason String or User Property yet. */
	rc = send__unsuback(context, mid, reason_code_count, reason_codes, NULL);
	mosquitto__free(reason_codes);
	return rc;
}