/** * Process retained messages (when a client subscribes) * @param client the client to send the messages to * @param topic the topic to match * @param qos the QoS of the subscription */ void MQTTProtocol_processRetaineds(Clients* client, char* topic, int qos, int priority) { List* rpl = NULL; ListElement* currp = NULL; #if defined(QOS0_SEND_LIMIT) int qos0count = 0; #endif FUNC_ENTRY; rpl = SubscriptionEngines_getRetained(bstate->se, topic); while (ListNextElement(rpl, &currp)) { int curqos; Publish publish; Messages* p = NULL; RetainedPublications* rp = (RetainedPublications*)(currp->content); publish.payload = rp->payload; publish.payloadlen = rp->payloadlen; publish.topic = rp->topicName; curqos = (rp->qos < qos) ? rp->qos : qos; #if defined(QOS0_SEND_LIMIT) if (curqos == 0) ++qos0count; if (qos0count > bstate->max_inflight_messages) /* a somewhat arbitrary criterion */ { if (MQTTProtocol_queuePublish(client, &publish, curqos, 1, priority, &p) == SOCKET_ERROR) break; } else { #endif if (Protocol_startOrQueuePublish(client, &publish, curqos, 1, priority, &p) == SOCKET_ERROR) break; #if defined(QOS0_SEND_LIMIT) } #endif } ListFreeNoContent(rpl); FUNC_EXIT; }
/** * 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, ¤t)) { 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; }