/** * 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; }
/** * 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; }