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