From 385798f9baff0d9e3bfd98807f95690361f93c84 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 21 May 2015 22:04:26 +0200 Subject: [PATCH] Only add Entity Capabilities extension to available presences Also don't override eventually send presences on updateLocalEntityCaps(), instead save the last sent Presence stanza and re-send that stanza. SMACK-669. --- .../jivesoftware/smack/packet/Presence.java | 13 ++++++++++ .../org/jivesoftware/smack/packet/Stanza.java | 17 ++++++++++++ .../smackx/caps/EntityCapsManager.java | 26 ++++++++++--------- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java index 5781e94c0..e596e53ea 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java @@ -19,6 +19,7 @@ package org.jivesoftware.smack.packet; import java.util.Locale; +import org.jivesoftware.smack.packet.id.StanzaIdUtil; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.TypedCloneable; import org.jivesoftware.smack.util.XmlStringBuilder; @@ -259,6 +260,18 @@ public final class Presence extends Stanza implements TypedCloneable { return new Presence(this); } + /** + * Clone this presence and set a newly generated stanza ID as the clone's ID. + * + * @return a "clone" of this presence with a different stanza ID. + * @since 4.1.2 + */ + public Presence cloneWithNewId() { + Presence clone = clone(); + clone.setStanzaId(StanzaIdUtil.newStanzaId()); + return clone; + } + /** * An enum to represent the presence type. Note that presence type is often confused * with presence mode. Generally, if a user is signed in to a server, they have a presence diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java index d256b2d3f..c13f831e3 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java @@ -316,6 +316,23 @@ public abstract class Stanza implements TopLevelStreamElement, Packet { } } + /** + * Add the given extension and override eventually existing extensions with the same name and + * namespace. + * + * @param extension the extension element to add. + * @return one of the removed extensions or null if there are none. + * @since 4.1.2 + */ + public ExtensionElement overrideExtension(ExtensionElement extension) { + if (extension == null) return null; + synchronized (packetExtensions) { + ExtensionElement removedExtension = removeExtension(extension); + addExtension(extension); + return removedExtension; + } + } + /** * Adds a collection of stanza(/packet) extensions to the packet. Does nothing if extensions is null. * diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index 5bd2880fd..0818b7b87 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -30,6 +30,7 @@ import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.filter.NotFilter; +import org.jivesoftware.smack.filter.PresenceTypeFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; @@ -94,7 +95,6 @@ public class EntityCapsManager extends Manager { ELEMENT, NAMESPACE)); private static final StanzaFilter PRESENCES_WITHOUT_CAPS = new AndFilter(new StanzaTypeFilter(Presence.class), new NotFilter(new StanzaExtensionFilter( ELEMENT, NAMESPACE))); - private static final StanzaFilter PRESENCES = StanzaTypeFilter.PRESENCE; /** * Map of "node + '#' + hash" to DiscoverInfo data @@ -262,7 +262,7 @@ public class EntityCapsManager extends Manager { private boolean entityCapsEnabled; private CapsVersionAndHash currentCapsVersion; - private boolean presenceSend = false; + private volatile Presence presenceSend; /** * The entity node String used by this EntityCapsManager instance. @@ -291,7 +291,7 @@ public class EntityCapsManager extends Manager { // Reset presenceSend when the connection was not resumed if (!resumed) { - presenceSend = false; + presenceSend = null; } } private void processCapsStreamFeatureIfAvailable(XMPPConnection connection) { @@ -339,23 +339,26 @@ public class EntityCapsManager extends Manager { connection.addPacketSendingListener(new StanzaListener() { @Override public void processPacket(Stanza packet) { - presenceSend = true; + presenceSend = (Presence) packet; } - }, PRESENCES); + }, PresenceTypeFilter.AVAILABLE); // Intercept presence packages and add caps data when intended. // XEP-0115 specifies that a client SHOULD include entity capabilities // with every presence notification it sends. StanzaListener packetInterceptor = new StanzaListener() { public void processPacket(Stanza packet) { - if (!entityCapsEnabled) + if (!entityCapsEnabled) { + // Be sure to not send stanzas with the caps extension if it's not enabled + packet.removeExtension(CapsExtension.ELEMENT, CapsExtension.NAMESPACE); return; + } CapsVersionAndHash capsVersionAndHash = getCapsVersionAndHash(); CapsExtension caps = new CapsExtension(entityNode, capsVersionAndHash.version, capsVersionAndHash.hash); - packet.addExtension(caps); + packet.overrideExtension(caps); } }; - connection.addPacketInterceptor(packetInterceptor, PRESENCES); + connection.addPacketInterceptor(packetInterceptor, PresenceTypeFilter.AVAILABLE); // It's important to do this as last action. Since it changes the // behavior of the SDM in some ways sdm.setEntityCapsManager(this); @@ -502,15 +505,14 @@ public class EntityCapsManager extends Manager { } }); - // Send an empty presence, and let the packet interceptor + // Re-send the last sent presence, and let the stanza interceptor // add a node to it. // See http://xmpp.org/extensions/xep-0115.html#advertise // We only send a presence packet if there was already one send // to respect ConnectionConfiguration.isSendPresence() - if (connection != null && connection.isAuthenticated() && presenceSend) { - Presence presence = new Presence(Presence.Type.available); + if (connection != null && connection.isAuthenticated() && presenceSend != null) { try { - connection.sendStanza(presence); + connection.sendStanza(presenceSend.cloneWithNewId()); } catch (NotConnectedException e) { LOGGER.log(Level.WARNING, "Could could not update presence with caps info", e);