mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-09 17:19:39 +02:00
Add support for "Caps Optimizations"
Smack's previous entity caps implementation assumed that an entity lost its entity caps feature as soon as a presence without caps from that entity was received. But according to XEP-0115 § 8.4, this is a perfectly normal optimization technique. We now reset the caps state after an available presence becomes unavailable. Also introduce PresenceEventListener, which is required for this feature. Also make Roster.preApprove() take a BareJid as argument. Fixes SMACK-723.
This commit is contained in:
parent
4248fbbb89
commit
d07ed60737
8 changed files with 280 additions and 22 deletions
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2016 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.roster;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.FullJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
|
||||
public abstract class AbstractPresenceEventListener implements PresenceEventListener {
|
||||
|
||||
@Override
|
||||
public void presenceAvailable(FullJid address, Presence availablePresence) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceUnavailable(FullJid address, Presence presence) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceError(Jid address, Presence errorPresence) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceSubscribed(BareJid address, Presence subscribedPresence) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence) {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2016 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.roster;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.FullJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
|
||||
public interface PresenceEventListener {
|
||||
|
||||
public void presenceAvailable(FullJid address, Presence availablePresence);
|
||||
|
||||
public void presenceUnavailable(FullJid address, Presence presence);
|
||||
|
||||
public void presenceError(Jid address, Presence errorPresence);
|
||||
|
||||
public void presenceSubscribed(BareJid address, Presence subscribedPresence);
|
||||
|
||||
public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software.
|
||||
* Copyright 2003-2007 Jive Software, 2016 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -151,6 +151,8 @@ public final class Roster extends Manager {
|
|||
private final Set<RosterEntry> unfiledEntries = new CopyOnWriteArraySet<>();
|
||||
private final Set<RosterListener> rosterListeners = new LinkedHashSet<>();
|
||||
|
||||
private final Set<PresenceEventListener> presenceEventListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
/**
|
||||
* A map of JIDs to another Map of Resourceparts to Presences. The 'inner' map may contain
|
||||
* {@link Resourcepart#EMPTY} if there are no other Presences available.
|
||||
|
@ -289,6 +291,11 @@ public final class Roster extends Manager {
|
|||
if (resumed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that all available presences received so far in a eventually existing previous session are
|
||||
// marked 'offline'.
|
||||
setOfflinePresencesAndResetLoaded();
|
||||
|
||||
try {
|
||||
Roster.this.reload();
|
||||
}
|
||||
|
@ -532,6 +539,14 @@ public final class Roster extends Manager {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean addPresenceEventListener(PresenceEventListener presenceEventListener) {
|
||||
return presenceEventListeners.add(presenceEventListener);
|
||||
}
|
||||
|
||||
public boolean removePresenceEventListener(PresenceEventListener presenceEventListener) {
|
||||
return presenceEventListeners.remove(presenceEventListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group.
|
||||
* <p>
|
||||
|
@ -584,10 +599,7 @@ public final class Roster extends Manager {
|
|||
rosterPacket.addRosterItem(item);
|
||||
connection.createPacketCollectorAndSend(rosterPacket).nextResultOrThrow();
|
||||
|
||||
// Create a presence subscription packet and send.
|
||||
Presence presencePacket = new Presence(Presence.Type.subscribe);
|
||||
presencePacket.setTo(user);
|
||||
connection.sendStanza(presencePacket);
|
||||
sendSubscriptionRequest(user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -621,7 +633,7 @@ public final class Roster extends Manager {
|
|||
* @throws FeatureNotSupportedException if pre-approving is not supported.
|
||||
* @since 4.2
|
||||
*/
|
||||
public void preApprove(Jid user) throws NotLoggedInException, NotConnectedException, InterruptedException, FeatureNotSupportedException {
|
||||
public void preApprove(BareJid user) throws NotLoggedInException, NotConnectedException, InterruptedException, FeatureNotSupportedException {
|
||||
final XMPPConnection connection = connection();
|
||||
if (!isSubscriptionPreApprovalSupported()) {
|
||||
throw new FeatureNotSupportedException("Pre-approving");
|
||||
|
@ -644,6 +656,15 @@ public final class Roster extends Manager {
|
|||
return connection.hasFeature(SubscriptionPreApproval.ELEMENT, SubscriptionPreApproval.NAMESPACE);
|
||||
}
|
||||
|
||||
public void sendSubscriptionRequest(BareJid jid) throws NotLoggedInException, NotConnectedException, InterruptedException {
|
||||
final XMPPConnection connection = getAuthenticatedConnectionOrThrow();
|
||||
|
||||
// Create a presence subscription packet and send.
|
||||
Presence presencePacket = new Presence(Presence.Type.subscribe);
|
||||
presencePacket.setTo(jid);
|
||||
connection.sendStanza(presencePacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the subscribe listener, which is invoked on incoming subscription requests and if
|
||||
* {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. If
|
||||
|
@ -1363,12 +1384,21 @@ public final class Roster extends Manager {
|
|||
Presence presence = (Presence) packet;
|
||||
Jid from = presence.getFrom();
|
||||
Resourcepart fromResource = Resourcepart.EMPTY;
|
||||
BareJid bareFrom = null;
|
||||
FullJid fullFrom = null;
|
||||
if (from != null) {
|
||||
fromResource = from.getResourceOrNull();
|
||||
if (fromResource == null) {
|
||||
fromResource = Resourcepart.EMPTY;
|
||||
bareFrom = from.asBareJid();
|
||||
}
|
||||
else {
|
||||
fullFrom = from.asFullJidIfPossible();
|
||||
// We know that this must be a full JID in this case.
|
||||
assert (fullFrom != null);
|
||||
}
|
||||
}
|
||||
|
||||
BareJid key = from != null ? from.asBareJid() : null;
|
||||
Map<Resourcepart, Presence> userPresences;
|
||||
|
||||
|
@ -1388,6 +1418,9 @@ public final class Roster extends Manager {
|
|||
if (contains(key)) {
|
||||
fireRosterPresenceEvent(presence);
|
||||
}
|
||||
for (PresenceEventListener presenceEventListener : presenceEventListeners) {
|
||||
presenceEventListener.presenceAvailable(fullFrom, presence);
|
||||
}
|
||||
break;
|
||||
// If an "unavailable" packet.
|
||||
case unavailable:
|
||||
|
@ -1409,6 +1442,23 @@ public final class Roster extends Manager {
|
|||
if (contains(key)) {
|
||||
fireRosterPresenceEvent(presence);
|
||||
}
|
||||
|
||||
// Ensure that 'from' is a full JID before invoking the presence unavailable
|
||||
// listeners. Usually unavailable presences always have a resourcepart, i.e. are
|
||||
// full JIDs, but RFC 6121 § 4.5.4 has an implementation note that unavailable
|
||||
// presences from a bare JID SHOULD be treated as applying to all resources. I don't
|
||||
// think any client or server ever implemented that, I do think that this
|
||||
// implementation note is a terrible idea since it adds another corner case in
|
||||
// client code, instead of just having the invariant
|
||||
// "unavailable presences are always from the full JID".
|
||||
if (fullFrom != null) {
|
||||
for (PresenceEventListener presenceEventListener : presenceEventListeners) {
|
||||
presenceEventListener.presenceUnavailable(fullFrom, presence);
|
||||
}
|
||||
} else {
|
||||
LOGGER.fine("Unavailable presence from bare JID: " + presence);
|
||||
}
|
||||
|
||||
break;
|
||||
// Error presence packets from a bare JID mean we invalidate all existing
|
||||
// presence info for the user.
|
||||
|
@ -1429,6 +1479,19 @@ public final class Roster extends Manager {
|
|||
if (contains(key)) {
|
||||
fireRosterPresenceEvent(presence);
|
||||
}
|
||||
for (PresenceEventListener presenceEventListener : presenceEventListeners) {
|
||||
presenceEventListener.presenceError(from, presence);
|
||||
}
|
||||
break;
|
||||
case subscribed:
|
||||
for (PresenceEventListener presenceEventListener : presenceEventListeners) {
|
||||
presenceEventListener.presenceSubscribed(bareFrom, presence);
|
||||
}
|
||||
break;
|
||||
case unsubscribed:
|
||||
for (PresenceEventListener presenceEventListener : presenceEventListeners) {
|
||||
presenceEventListener.presenceUnsubscribed(bareFrom, presence);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1624,4 +1687,5 @@ public final class Roster extends Manager {
|
|||
public void setNonRosterPresenceMapMaxSize(int maximumSize) {
|
||||
nonRosterPresenceMap.setMaxCacheSize(maximumSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ public class SubscriptionPreApprovalTest extends InitSmackIm {
|
|||
@Test(expected=FeatureNotSupportedException.class)
|
||||
public void testPreApprovalNotSupported() throws Throwable {
|
||||
final Jid contactJID = JidCreate.from("preapproval@example.com");
|
||||
roster.preApprove(contactJID);
|
||||
roster.preApprove(contactJID.asBareJid());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue