mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-09 17:19:39 +02:00
Add support for IoT Friend approvals
This adds supports for an experimental protocol flow where a pending friend request's decission is later on deliverd to the requestor after the owner made its decission.
This commit is contained in:
parent
5a2326a856
commit
1d3c48e6ce
11 changed files with 407 additions and 56 deletions
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
*
|
||||
* 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.smackx.iot.provisioning;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
||||
public interface BecameFriendListener {
|
||||
|
||||
void becameFriend(BareJid jid, Presence presence);
|
||||
|
||||
}
|
|
@ -18,7 +18,9 @@ package org.jivesoftware.smackx.iot.provisioning;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -41,6 +43,7 @@ import org.jivesoftware.smack.packet.IQ.Type;
|
|||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.roster.AbstractPresenceEventListener;
|
||||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jivesoftware.smack.roster.RosterEntry;
|
||||
import org.jivesoftware.smack.roster.SubscribeListener;
|
||||
|
@ -50,6 +53,7 @@ import org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager;
|
|||
import org.jivesoftware.smackx.iot.provisioning.element.ClearCache;
|
||||
import org.jivesoftware.smackx.iot.provisioning.element.ClearCacheResponse;
|
||||
import org.jivesoftware.smackx.iot.provisioning.element.Constants;
|
||||
import org.jivesoftware.smackx.iot.provisioning.element.Friend;
|
||||
import org.jivesoftware.smackx.iot.provisioning.element.IoTIsFriend;
|
||||
import org.jivesoftware.smackx.iot.provisioning.element.IoTIsFriendResponse;
|
||||
import org.jivesoftware.smackx.iot.provisioning.element.Unfriend;
|
||||
|
@ -68,6 +72,8 @@ public final class IoTProvisioningManager extends Manager {
|
|||
|
||||
private static final Logger LOGGER = Logger.getLogger(IoTProvisioningManager.class.getName());
|
||||
|
||||
private static final StanzaFilter FRIEND_MESSAGE = new AndFilter(StanzaTypeFilter.MESSAGE,
|
||||
new StanzaExtensionFilter(Friend.ELEMENT, Friend.NAMESPACE));
|
||||
private static final StanzaFilter UNFRIEND_MESSAGE = new AndFilter(StanzaTypeFilter.MESSAGE,
|
||||
new StanzaExtensionFilter(Unfriend.ELEMENT, Unfriend.NAMESPACE));
|
||||
|
||||
|
@ -99,6 +105,13 @@ public final class IoTProvisioningManager extends Manager {
|
|||
|
||||
private final Roster roster;
|
||||
private final LruCache<Jid, LruCache<BareJid, Void>> negativeFriendshipRequestCache = new LruCache<>(8);
|
||||
private final LruCache<BareJid, Void> friendshipDeniedCache = new LruCache<>(16);
|
||||
|
||||
private final LruCache<BareJid, Void> friendshipRequestedCache = new LruCache<>(16);
|
||||
|
||||
private final Set<BecameFriendListener> becameFriendListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
private final Set<WasUnfriendedListener> wasUnfriendedListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
private Jid configuredProvisioningServer;
|
||||
|
||||
|
@ -129,6 +142,47 @@ public final class IoTProvisioningManager extends Manager {
|
|||
}
|
||||
}, UNFRIEND_MESSAGE);
|
||||
|
||||
// Stanza listener for XEP-0324 § 3.2.4.
|
||||
connection.addAsyncStanzaListener(new StanzaListener() {
|
||||
@Override
|
||||
public void processPacket(final Stanza stanza) throws NotConnectedException, InterruptedException {
|
||||
final Message friendMessage = (Message) stanza;
|
||||
final Friend friend = Friend.from(friendMessage);
|
||||
final BareJid friendJid = friend.getFriend();
|
||||
|
||||
if (isFromProvisioningService(friendMessage)) {
|
||||
// We received a recommendation from a provisioning server.
|
||||
// Notify the recommended friend that we will now accept his
|
||||
// friendship requests.
|
||||
final XMPPConnection connection = connection();
|
||||
Friend friendNotifiacation = new Friend(connection.getUser().asBareJid());
|
||||
Message notificationMessage = new Message(friendJid, friendNotifiacation);
|
||||
connection.sendStanza(notificationMessage);
|
||||
} else {
|
||||
// Check is the message was send from a thing we previously
|
||||
// tried to become friends with. If this is the case, then
|
||||
// thing is likely telling us that we can become now
|
||||
// friends.
|
||||
Jid from = friendMessage.getFrom();
|
||||
if (!friendshipDeniedCache.containsKey(from)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BareJid bareFrom = from.asBareJid();
|
||||
// Sanity check: If a thing recommends us itself as friend,
|
||||
// which should be the case once we reach this code, then
|
||||
// the bare 'from' JID should be equals to the JID of the
|
||||
// recommended friend.
|
||||
if (!bareFrom.equals(friendJid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-try the friendship request.
|
||||
sendFriendshipRequest(friendJid);
|
||||
}
|
||||
}
|
||||
}, FRIEND_MESSAGE);
|
||||
|
||||
connection.registerIQRequestHandler(
|
||||
new AbstractIqRequestHandler(ClearCache.ELEMENT, ClearCache.NAMESPACE, Type.set, Mode.async) {
|
||||
@Override
|
||||
|
@ -193,6 +247,25 @@ public final class IoTProvisioningManager extends Manager {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
roster.addPresenceEventListener(new AbstractPresenceEventListener() {
|
||||
@Override
|
||||
public void presenceSubscribed(BareJid address, Presence subscribedPresence) {
|
||||
friendshipRequestedCache.remove(address);
|
||||
for (BecameFriendListener becameFriendListener : becameFriendListeners) {
|
||||
becameFriendListener.becameFriend(address, subscribedPresence);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence) {
|
||||
if (friendshipRequestedCache.containsKey(address)) {
|
||||
friendshipDeniedCache.put(address, null);
|
||||
}
|
||||
for (WasUnfriendedListener wasUnfriendedListener : wasUnfriendedListeners) {
|
||||
wasUnfriendedListener.wasUnfriendedListener(address, unsubscribedPresence);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -272,6 +345,9 @@ public final class IoTProvisioningManager extends Manager {
|
|||
public void sendFriendshipRequest(BareJid bareJid) throws NotConnectedException, InterruptedException {
|
||||
Presence presence = new Presence(Presence.Type.subscribe);
|
||||
presence.setTo(bareJid);
|
||||
|
||||
friendshipRequestedCache.put(bareJid, null);
|
||||
|
||||
connection().sendStanza(presence);
|
||||
}
|
||||
|
||||
|
@ -295,6 +371,22 @@ public final class IoTProvisioningManager extends Manager {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean addBecameFriendListener(BecameFriendListener becameFriendListener) {
|
||||
return becameFriendListeners.add(becameFriendListener);
|
||||
}
|
||||
|
||||
public boolean removeBecameFriendListener(BecameFriendListener becameFriendListener) {
|
||||
return becameFriendListeners.remove(becameFriendListener);
|
||||
}
|
||||
|
||||
public boolean addWasUnfriendedListener(WasUnfriendedListener wasUnfriendedListener) {
|
||||
return wasUnfriendedListeners.add(wasUnfriendedListener);
|
||||
}
|
||||
|
||||
public boolean removeWasUnfriendedListener(WasUnfriendedListener wasUnfriendedListener) {
|
||||
return wasUnfriendedListeners.remove(wasUnfriendedListener);
|
||||
}
|
||||
|
||||
private boolean isFromProvisioningService(Stanza stanza) {
|
||||
Jid provisioningServer;
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
*
|
||||
* 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.smackx.iot.provisioning;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
||||
public interface WasUnfriendedListener {
|
||||
|
||||
void wasUnfriendedListener(BareJid jid, Presence presence);
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
*
|
||||
* 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.smackx.iot.provisioning.element;
|
||||
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
||||
public class Friend implements ExtensionElement {
|
||||
|
||||
public static final String ELEMENT = "friend";
|
||||
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
|
||||
|
||||
private final BareJid friend;
|
||||
|
||||
public Friend(BareJid friend) {
|
||||
this.friend = Objects.requireNonNull(friend, "Friend must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.attribute("jid", friend);
|
||||
xml.closeEmptyElement();
|
||||
return xml;
|
||||
}
|
||||
|
||||
public BareJid getFriend() {
|
||||
return friend;
|
||||
}
|
||||
|
||||
public static Friend from(Message message) {
|
||||
return message.getExtension(ELEMENT, NAMESPACE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
*
|
||||
* 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.smackx.iot.provisioning.provider;
|
||||
|
||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||
import org.jivesoftware.smack.util.ParserUtils;
|
||||
import org.jivesoftware.smackx.iot.provisioning.element.Friend;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
public class FriendProvider extends ExtensionElementProvider<Friend> {
|
||||
|
||||
@Override
|
||||
public Friend parse(XmlPullParser parser, int initialDepth) throws XmppStringprepException {
|
||||
BareJid jid = ParserUtils.getBareJidAttribute(parser);
|
||||
return new Friend(jid);
|
||||
}
|
||||
|
||||
}
|
|
@ -132,6 +132,11 @@
|
|||
<namespace>urn:xmpp:iot:provisioning</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.provisioning.provider.ClearCacheResponseProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>friend</elementName>
|
||||
<namespace>urn:xmpp:iot:provisioning</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.provisioning.provider.FriendProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>unfriend</elementName>
|
||||
<namespace>urn:xmpp:iot:provisioning</namespace>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue