package org.limewire.xmpp.client.impl; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence.Type; import org.limewire.friend.api.FriendRequest; import org.limewire.friend.api.FriendRequestDecisionHandler; import org.limewire.friend.api.FriendRequestEvent; import org.limewire.friend.impl.util.PresenceUtils; import org.limewire.listener.EventBroadcaster; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; /** * Handles presence subscriptions and unsubscriptions on an XMPP connection * (see RFC 3921 section 8). <code>FriendRequestEvent</code>s that require * decisions from the user are broadcast for interception by the UI, and the * results are passed back asynchronously through the * <code>FriendRequestDecisionHandler</code> interface. */ class SubscriptionListener implements PacketListener, PacketFilter, FriendRequestDecisionHandler { private static final Log LOG = LogFactory.getLog(SubscriptionListener.class); private final XMPPConnection connection; private final EventBroadcaster<FriendRequestEvent> friendRequestBroadcaster; SubscriptionListener(XMPPConnection connection, EventBroadcaster<FriendRequestEvent> friendRequestBroadcaster) { this.connection = connection; this.friendRequestBroadcaster = friendRequestBroadcaster; } @Override public void processPacket(Packet packet) { try { Presence presence = (Presence)packet; String friendUsername = PresenceUtils.parseBareAddress(packet.getFrom()); if(presence.getType() == Type.subscribe) { LOG.debugf("subscribe from {0}", friendUsername); // If this is a new friend request, ask the user what to do Roster roster = connection.getRoster(); if(roster != null) { RosterEntry entry = roster.getEntry(friendUsername); if(entry == null) { LOG.debug("it's a new subscription"); // Ask the user friendRequestBroadcaster.broadcast(new FriendRequestEvent( new FriendRequest(friendUsername, this), FriendRequestEvent.Type.REQUESTED)); } else { LOG.debug("it's a response to our subscription"); // Acknowledge the subscription Presence subbed = new Presence(Presence.Type.subscribed); subbed.setTo(friendUsername); connection.sendPacket(subbed); } } } else if(presence.getType() == Type.subscribed) { LOG.debugf("subscribed from {0}", friendUsername); } else if(presence.getType() == Type.unsubscribe) { LOG.debugf("unsubscribe from {0}", friendUsername); // Acknowledge the unsubscription Presence unsubbed = new Presence(Presence.Type.unsubscribed); unsubbed.setTo(friendUsername); connection.sendPacket(unsubbed); // If this is a response, don't respond again Roster roster = connection.getRoster(); if(roster != null) { RosterEntry entry = roster.getEntry(friendUsername); if(entry == null) { LOG.debug("it's a response to our unsubscription"); } else { LOG.debug("it's a new unsubscription"); // Unsubscribe from the friend Presence unsub = new Presence(Presence.Type.unsubscribe); unsub.setTo(friendUsername); connection.sendPacket(unsub); // Remove the friend from the roster try { roster.removeEntry(entry); } catch(XMPPException x) { LOG.debug(x); } } } } else if(presence.getType() == Type.unsubscribed) { LOG.debugf("unsubscribed from {0}", friendUsername); } } catch (XMPPException e) { LOG.debug("processPacket failed", e); } } @Override public boolean accept(Packet packet) { if(packet instanceof Presence) { Presence presence = (Presence)packet; return presence.getType() == Type.subscribe || presence.getType() == Type.subscribed || presence.getType() == Type.unsubscribe || presence.getType() == Type.unsubscribed; } return false; } @Override public void handleDecision(String friendUsername, boolean accepted) { try { if(!connection.isConnected()) return; if(accepted) { LOG.debug("user accepted"); // Acknowledge the subscription Presence subbed = new Presence(Presence.Type.subscribed); subbed.setTo(friendUsername); connection.sendPacket(subbed); // Add the friend to the roster (this will subscribe to the friend) Roster roster = connection.getRoster(); if(roster != null) { roster.createEntry(friendUsername, friendUsername, null); } } else { LOG.debug("user declined"); // Refuse the subscription Presence unsubbed = new Presence(Presence.Type.unsubscribed); unsubbed.setTo(friendUsername); connection.sendPacket(unsubbed); } } catch (XMPPException e) { LOG.debug("handleDecision failed", e); } } }