From 533695c1b428684a0675853421d708f612411f69 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 1 Apr 2021 13:57:36 +0200 Subject: [PATCH] [core] Properly reply to IQ requests Properly reply to IQ requests with feature-not-implemented or service-unavailable. In case there is no IQ request handler but the IQ namespace is known, we reply with feature-not-implemented, otherwise with service-unavailable. --- .../smack/AbstractXMPPConnection.java | 59 ++++++++++++++++--- .../smack/SmackConfiguration.java | 7 +-- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index f12ff85e0..3c2f9f21b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -377,6 +377,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { private final Map setIqRequestHandler = new HashMap<>(); private final Map getIqRequestHandler = new HashMap<>(); + private final Set iqRequestHandlerNamespaces = new CopyOnWriteArraySet<>(); + private final Map iqRequestHandlerNamespacesReferenceCounters = new HashMap<>(); private final StanzaFactory stanzaFactory; @@ -1524,15 +1526,18 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { throw new IllegalStateException("Should only encounter IQ type 'get' or 'set'"); } if (iqRequestHandler == null) { + final String iqNamespace = key.getNamespaceURI(); StanzaError.Condition replyCondition; switch (unknownIqRequestReplyMode) { case doNotReply: return; - case replyFeatureNotImplemented: - replyCondition = StanzaError.Condition.feature_not_implemented; - break; - case replyServiceUnavailable: - replyCondition = StanzaError.Condition.service_unavailable; + case reply: + boolean isKnownNamespace = iqRequestHandlerNamespaces.contains(iqNamespace); + if (isKnownNamespace) { + replyCondition = StanzaError.Condition.feature_not_implemented; + } else { + replyCondition = StanzaError.Condition.service_unavailable; + } break; default: throw new AssertionError(); @@ -2043,18 +2048,35 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { @Override public IQRequestHandler registerIQRequestHandler(final IQRequestHandler iqRequestHandler) { final QName key = iqRequestHandler.getQName(); + IQRequestHandler previous; switch (iqRequestHandler.getType()) { case set: synchronized (setIqRequestHandler) { - return setIqRequestHandler.put(key, iqRequestHandler); + previous = setIqRequestHandler.put(key, iqRequestHandler); } + break; case get: synchronized (getIqRequestHandler) { - return getIqRequestHandler.put(key, iqRequestHandler); + previous = getIqRequestHandler.put(key, iqRequestHandler); } + break; default: throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed"); } + + final String iqNamespace = key.getNamespaceURI(); + synchronized (iqRequestHandlerNamespacesReferenceCounters) { + Integer newValue; + Integer counter = iqRequestHandlerNamespacesReferenceCounters.get(iqNamespace); + if (counter == null) { + iqRequestHandlerNamespaces.add(iqNamespace); + newValue = 0; + } else { + newValue = counter.intValue() + 1; + } + iqRequestHandlerNamespacesReferenceCounters.put(iqNamespace, newValue); + } + return previous; } @Override @@ -2065,19 +2087,38 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { @Override public IQRequestHandler unregisterIQRequestHandler(String element, String namespace, IQ.Type type) { + IQRequestHandler unregisteredHandler; final QName key = new QName(namespace, element); switch (type) { case set: synchronized (setIqRequestHandler) { - return setIqRequestHandler.remove(key); + unregisteredHandler = setIqRequestHandler.remove(key); } + break; case get: synchronized (getIqRequestHandler) { - return getIqRequestHandler.remove(key); + unregisteredHandler = getIqRequestHandler.remove(key); } + break; default: throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed"); } + + if (unregisteredHandler == null) { + return null; + } + + synchronized (iqRequestHandlerNamespacesReferenceCounters) { + int newValue = iqRequestHandlerNamespacesReferenceCounters.get(namespace).intValue() - 1; + if (newValue == 0) { + iqRequestHandlerNamespacesReferenceCounters.remove(namespace); + iqRequestHandlerNamespaces.remove(namespace); + } else { + iqRequestHandlerNamespacesReferenceCounters.put(namespace, newValue); + } + } + + return unregisteredHandler; } private long lastStanzaReceived; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java index 746c611a0..b93ec12cc 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2018-2020 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2018-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -343,11 +343,10 @@ public final class SmackConfiguration { public enum UnknownIqRequestReplyMode { doNotReply, - replyFeatureNotImplemented, - replyServiceUnavailable, + reply, } - private static UnknownIqRequestReplyMode unknownIqRequestReplyMode = UnknownIqRequestReplyMode.replyFeatureNotImplemented; + private static UnknownIqRequestReplyMode unknownIqRequestReplyMode = UnknownIqRequestReplyMode.reply; public static UnknownIqRequestReplyMode getUnknownIqRequestReplyMode() { return unknownIqRequestReplyMode;