Esempio n. 1
0
/**
 * Originates a new publication - sends it to all clients subscribed.
 * @param publish pointer to a stucture which contains all the publication information
 * @param originator the originating client
 */
void Protocol_processPublication(Publish* publish, char* originator)
{
	Messages* stored = NULL; /* to avoid duplication of data where possible */
	List* clients;
	ListElement* current = NULL;
	int savedMsgId = publish->msgId;
	int clean_needed = 0;

	FUNC_ENTRY;
	
	if (Topics_hasWildcards(publish->topic))
	{
		Log(LOG_INFO, 12, NULL, publish->topic, originator);
		goto exit;
	}

	if ((strcmp(INTERNAL_CLIENTID, originator) != 0) && bstate->password_file && bstate->acl_file)
	{
		Clients* client = (Clients*)(TreeFindIndex(bstate->clients, originator, 1)->content);
		
		if (Users_authorise(client->user, publish->topic, ACL_WRITE) == false)
		{
			Log(LOG_AUDIT, 149, NULL, originator, publish->topic);
			goto exit;
		}
	}

	if (publish->header.bits.retain)
	{
		SubscriptionEngines_setRetained(bstate->se, publish->topic, publish->header.bits.qos, publish->payload, publish->payloadlen);
		if (bstate->persistence == 1 && bstate->autosave_on_changes == 1 && bstate->autosave_interval > 0
			&& bstate->se->retained_changes >= bstate->autosave_interval)
		{
			Log(LOG_INFO, 100, NULL, bstate->autosave_interval);
			SubscriptionEngines_save(bstate->se);
		}
	}

	clients = SubscriptionEngines_getSubscribers(bstate->se, publish->topic, originator);
	if (strncmp(publish->topic, "$SYS/client/", 12) == 0)
	{ /* default subscription for a client */
		Node* node = TreeFindIndex(bstate->clients, &publish->topic[12], 1);
		if (node == NULL)
			node = TreeFind(bstate->disconnected_clients, &publish->topic[12]);
		if (node && node->content)
		{
			Subscriptions* rcs = malloc(sizeof(Subscriptions));
			rcs->clientName = &publish->topic[12];
			rcs->qos = 2;
			rcs->priority = PRIORITY_NORMAL;
			rcs->topicName = publish->topic;
			ListAppend(clients, rcs, sizeof(Subscriptions));
		}
	}
	current = NULL;
	while (ListNextElement(clients, &current))
	{
		Node* curnode = NULL;
		unsigned int qos = ((Subscriptions*)(current->content))->qos;
		int priority = ((Subscriptions*)(current->content))->priority;
		char* clientName = ((Subscriptions*)(current->content))->clientName;
		
		if (publish->header.bits.qos < qos) /* reduce qos if > subscribed qos */
			qos = publish->header.bits.qos;
			
		if ((curnode = TreeFindIndex(bstate->clients, clientName, 1)) == NULL)
			curnode = TreeFind(bstate->disconnected_clients, clientName);
#if defined(MQTTS)
		if (curnode == NULL && ((curnode = TreeFindIndex(bstate->mqtts_clients, clientName, 1)) == NULL))
			curnode = TreeFind(bstate->disconnected_mqtts_clients, clientName);
#endif

		if (curnode)
		{
			Clients* pubclient = (Clients*)(curnode->content);
			int retained = 0;
			Messages* saved = NULL;
			char* original_topic = publish->topic;
			
#if !defined(NO_BRIDGE)
			if (pubclient->outbound || pubclient->noLocal)
			{
				retained = publish->header.bits.retain; /* outbound and noLocal mean outward/inward bridge client,
																							so keep retained flag */
				if (pubclient->outbound)
				{
					Bridge_handleOutbound(pubclient, publish);
					if (publish->topic != original_topic)
					{ /* handleOutbound has changed the topic, so we musn't used the stored pub which
					        contains the original topic */
						saved = stored;
						stored = NULL;
					}
				}
			}
#endif
			if (Protocol_startOrQueuePublish(pubclient, publish, qos, retained, priority, &stored) == SOCKET_ERROR)
			{
				pubclient->good = pubclient->connected = 0; /* flag this client as needing to be cleaned up */
				clean_needed = 1;
			}
			if (publish->topic != original_topic)
			{
				stored = saved;  /* restore the stored pointer for the next loop iteration */
				free(publish->topic);
				publish->topic = original_topic;
			}
		}
	}
	publish->msgId = savedMsgId;
	/* INTERNAL_CLIENTID means that we are publishing data to the log,
			and we don't want to interfere with other close processing */
	if (clean_needed && strcmp(originator, INTERNAL_CLIENTID) != 0)
		MQTTProtocol_clean_clients(bstate->clients);
	ListFree(clients);
exit:
	FUNC_EXIT;
}
Esempio n. 2
0
/**
 * Process an incoming ping subscribe packet for a socket
 * @param pack pointer to the subscribe packet
 * @param sock the socket on which the packet was received
 * @return completion code
 */
int MQTTProtocol_handleSubscribes(void* pack, int sock, Clients* client)
{
	int i, *aq, *isnew, *authorized;
	Subscribe* subscribe = (Subscribe*)pack;
	ListElement *curtopic = NULL, *curqos = NULL;
	int rc = TCPSOCKET_COMPLETE;
#if !defined(SINGLE_LISTENER)
	Listener* listener = Socket_getParentListener(sock);
#endif

	FUNC_ENTRY;
	if (Protocol_isClientQuiescing(client))
		goto exit; /* don't accept new work */

	Log(LOG_PROTOCOL, 6, NULL, sock, client->clientID, subscribe->msgId);
	aq = malloc(sizeof(int)*(subscribe->noTopics));
	isnew = malloc(sizeof(int)*(subscribe->noTopics));
	authorized = malloc(sizeof(int)*(subscribe->noTopics));
	for (i = 0; i < subscribe->noTopics; ++i)
	{
		int j;
		ListElement *duptopic = NULL;

		ListNextElement(subscribe->topics, &curtopic);
		aq[i] = *(int*)(ListNextElement(subscribe->qoss, &curqos)->content);

		/* The mount_point topic transformation must be done before the topic syntax validity check
		 * otherwise badly formed topics can get into the subscription engine.
		 */
#if !defined(SINGLE_LISTENER)
		if (listener && listener->mount_point)
		{
			char* temp = malloc(strlen((char*)(curtopic->content)) + strlen(listener->mount_point) + 1);
			strcpy(temp, listener->mount_point);
			strcat(temp, (char*)(curtopic->content));
			free((char*)(curtopic->content));
			curtopic->content = temp;
			subscribe->topics->size += strlen(listener->mount_point);
		}
#endif

		if (!Topics_isValidName((char*)curtopic->content))
		{
			Log(LOG_WARNING, 153, NULL, (char*)curtopic->content, client->clientID, client->addr);
			free(curtopic->content);
			continue;
		}

		authorized[i] = true;
		if (bstate->password_file && bstate->acl_file)
		{
			authorized[i] = Users_authorise(client->user,(char*)(curtopic->content),ACL_READ);
			if (!authorized[i])
				Log(LOG_AUDIT, 150, NULL, client->clientID, (char*)(curtopic->content));
		}

		for (j = 0; j < i; ++j)
		{
			char* prevtopic = (char*)(ListNextElement(subscribe->topics, &duptopic)->content);
			if (strcmp(prevtopic, (char*)(curtopic->content)) == 0)
				duptopic->content = curtopic->content;
		}
		isnew[i] = SubscriptionEngines_subscribe(bstate->se, client->clientID,
			(char*)(curtopic->content), aq[i], client->noLocal, (client->cleansession == 0), PRIORITY_NORMAL);
	}
	/* send suback before sending the retained publications because a lot of retained publications could fill up the socket buffer */
	if ((rc = MQTTPacket_send_suback(subscribe->msgId, subscribe->noTopics, aq, sock, client->clientID)) != SOCKET_ERROR)
	{
		curtopic = curqos = NULL;
		for (i = 0; i < subscribe->noTopics; ++i)
		{
			/* careful if you get >1 subscriptions using the same topic name in the same packet! */
			/* The next line changes the semantics of subscribe for bridge connections,
			 * so that retained messages are only sent for new subscriptions.  This is to help
			 * avoid "retained message storms" when a connection drops and is re-established.
			 * This change could be applied to all subscriptions by removing the "noLocal" check. */
			if (authorized[i] && ((client->noLocal == 0) || isnew[i]))
				MQTTProtocol_processRetaineds(client, (char*)(ListNextElement(subscribe->topics, &curtopic)->content),
					*(int*)(ListNextElement(subscribe->qoss, &curqos)->content), PRIORITY_NORMAL);
		}
	}
	free(aq);
	free(isnew);
	free(authorized);
exit:
	MQTTPacket_freeSubscribe(subscribe, 0);
	FUNC_EXIT_RC(rc);
	return rc;
}