/*
* Copyright 2006-2010 Daniel Henninger. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package net.sf.kraken.protocols.xmpp;
import net.sf.kraken.avatars.Avatar;
import net.sf.kraken.type.NameSpace;
import org.apache.log4j.Logger;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.packet.VCard;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.NotFoundException;
import org.xmpp.packet.Presence;
/**
* Presence packets are used for two purposes. First, to notify the server of
* our the clients current presence status. Second, they are used to subscribe
* and unsubscribe users from the roster.
*
* Instances of XMPPSubscriptionHandler
* are responsible for handling events generated by receiving
* both types of presence stanzas from the legacy domain.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class XMPPPresenceHandler implements PacketListener{
final static Logger Log = Logger.getLogger(XMPPPresenceHandler.class);
private final XMPPSession session;
/**
* Instantiates a new Presence handler for the given session.
*
* @param session
* The session for which to handle presence stanzas.
*/
public XMPPPresenceHandler(XMPPSession session) {
if (session == null) {
throw new IllegalArgumentException(
"Argument 'session' cannot be null.");
}
this.session = session;
}
/* (non-Javadoc)
* @see org.jivesoftware.smack.PacketListener#processPacket(org.jivesoftware.smack.packet.Packet)
*/
public void processPacket(Packet packet) {
if (!(packet instanceof org.jivesoftware.smack.packet.Presence)) {
throw new IllegalArgumentException(getClass().getName()
+ " can only be used to handle presence packets. "
+ "Please modify the caller code accordingly "
+ "(use a appropriate PacketFilter).");
}
final org.jivesoftware.smack.packet.Presence presence = (org.jivesoftware.smack.packet.Presence) packet;
final org.jivesoftware.smack.packet.Presence.Type type = presence.getType();
if (type.equals(org.jivesoftware.smack.packet.Presence.Type.available)
|| type.equals(org.jivesoftware.smack.packet.Presence.Type.unavailable)) {
handlePresenceMode(presence);
} else {
handlePresenceSubscription(presence);
}
}
/**
* Handles incoming presence stanzas that relate to presence status / mode
* changes. Ignores others.
*
* @param presence
* the stanza
*/
private void handlePresenceMode(
final org.jivesoftware.smack.packet.Presence presence) {
if (!session.getBuddyManager().isActivated()) {
session.getBuddyManager().storePendingStatus(session.getTransport().convertIDToJID(presence.getFrom()), ((XMPPTransport)session.getTransport()).convertXMPPStatusToGateway(presence.getType(), presence.getMode()), presence.getStatus());
}
else {
// TODO: Need to handle resources and priorities!
try {
final XMPPBuddy xmppBuddy = session.getBuddyManager().getBuddy(session.getTransport().convertIDToJID(presence.getFrom()));
Log.debug("XMPP: Presence changed detected type "+presence.getType()+" and mode "+presence.getMode()+" for "+presence.getFrom());
xmppBuddy.setPresenceAndStatus(
((XMPPTransport)session.getTransport()).convertXMPPStatusToGateway(presence.getType(), presence.getMode()),
presence.getStatus()
);
if (JiveGlobals.getBooleanProperty("plugin.gateway."+session.getTransport().getType()+".avatars", true)) {
PacketExtension pe = presence.getExtension("x", NameSpace.VCARD_TEMP_X_UPDATE);
if (pe != null) {
DefaultPacketExtension dpe = (DefaultPacketExtension)pe;
String hash = dpe.getValue("photo");
final String from = presence.getFrom();
if (hash != null) {
Avatar curAvatar = xmppBuddy.getAvatar();
if (curAvatar == null || !curAvatar.getLegacyIdentifier().equals(hash)) {
new Thread() {
@Override
public void run() {
VCard vcard = new VCard();
try {
vcard.load(session.conn, from);
xmppBuddy.setAvatar(new Avatar(xmppBuddy.getJID(), from, vcard.getAvatar()));
}
catch (XMPPException e) {
Log.debug("XMPP: Failed to load XMPP avatar: ", e);
}
catch (IllegalArgumentException e) {
Log.debug("XMPP: Got null avatar, ignoring.");
}
}
}.start();
}
}
}
}
}
catch (NotFoundException e) {
Log.debug("XMPP: Received presence notification for contact that's not in the buddy manager of user " + session.getJID() + ". GTalk is known to do this occasionally: "+presence.getFrom());
// We cannot add this buddy to the buddy manager, as that would result into an auto-accept of the contact sending the data.
}
}
}
/**
* Handles incoming presence stanzas that relate to subscription status.
* Ignores others.
*
* @param presence
* the stanza
*/
private void handlePresenceSubscription(
final org.jivesoftware.smack.packet.Presence presence) {
final Presence p = new Presence();
p.setTo(session.getJID());
p.setFrom(session.getTransport().convertIDToJID(presence.getFrom()));
switch (presence.getType()) {
case subscribe:
p.setType(Presence.Type.subscribe);
break;
case subscribed:
final XMPPBuddy buddy = new XMPPBuddy(session.getBuddyManager(), presence.getFrom());
session.getBuddyManager().storeBuddy(buddy);
p.setType(Presence.Type.subscribed);
break;
case unsubscribe:
p.setType(Presence.Type.unsubscribe);
break;
case unsubscribed:
p.setType(Presence.Type.unsubscribed);
break;
case error:
p.setType(Presence.Type.error);
break;
default:
// don't send anything.
return;
}
session.getTransport().sendPacket(p);
}
}