1
0
Fork 0
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:
Florian Schmaus 2016-10-31 12:24:05 +01:00
parent 5a2326a856
commit 1d3c48e6ce
11 changed files with 407 additions and 56 deletions

View file

@ -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);
}

View file

@ -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 {

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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>