using System; using System.Collections.Generic; using Microsoft.Extensions.Options; using MQTTnet.Core.Packets; using MQTTnet.Core.Protocol; namespace MQTTnet.Core.Server { public sealed class MqttClientSubscriptionsManager { private readonly Dictionary _subscriptions = new Dictionary(); private readonly MqttServerOptions _options; public MqttClientSubscriptionsManager(IOptions options) { _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); } public MqttClientSubscribeResult Subscribe(MqttSubscribePacket subscribePacket, string clientId) { if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); var responsePacket = subscribePacket.CreateResponse(); var closeConnection = false; lock (_subscriptions) { foreach (var topicFilter in subscribePacket.TopicFilters) { var interceptorContext = new MqttSubscriptionInterceptorContext(clientId, topicFilter); _options.SubscriptionsInterceptor?.Invoke(interceptorContext); responsePacket.SubscribeReturnCodes.Add(interceptorContext.AcceptSubscription ? MqttSubscribeReturnCode.SuccessMaximumQoS1 : MqttSubscribeReturnCode.Failure); if (interceptorContext.CloseConnection) { closeConnection = true; } if (interceptorContext.AcceptSubscription) { _subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel; } } } return new MqttClientSubscribeResult { ResponsePacket = responsePacket, CloseConnection = closeConnection }; } public MqttUnsubAckPacket Unsubscribe(MqttUnsubscribePacket unsubscribePacket) { if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); lock (_subscriptions) { foreach (var topicFilter in unsubscribePacket.TopicFilters) { _subscriptions.Remove(topicFilter); } } return unsubscribePacket.CreateResponse(); } public CheckSubscriptionsResult CheckSubscriptions(MqttPublishPacket publishPacket) { if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); lock (_subscriptions) { foreach (var subscription in _subscriptions) { if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscription.Key)) { continue; } var effectiveQos = subscription.Value; if (publishPacket.QualityOfServiceLevel < effectiveQos) { effectiveQos = publishPacket.QualityOfServiceLevel; } return new CheckSubscriptionsResult { IsSubscribed = true, QualityOfServiceLevel = effectiveQos }; } } return new CheckSubscriptionsResult { IsSubscribed = false }; } } }