/*
* 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 java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Date;
import java.util.Collection;
import net.sf.kraken.BaseTransport;
import net.sf.kraken.protocols.xmpp.packet.AttentionExtension;
import net.sf.kraken.protocols.xmpp.packet.GoogleMailBoxPacket;
import net.sf.kraken.protocols.xmpp.packet.GoogleMailNotifyExtension;
import net.sf.kraken.protocols.xmpp.packet.GoogleMailSender;
import net.sf.kraken.protocols.xmpp.packet.GoogleMailThread;
import net.sf.kraken.protocols.xmpp.packet.GoogleNewMailExtension;
import net.sf.kraken.protocols.xmpp.packet.IQWithPacketExtension;
import net.sf.kraken.protocols.xmpp.packet.ProbePacket;
import net.sf.kraken.type.ChatStateType;
import net.sf.kraken.type.ConnectionFailureReason;
import net.sf.kraken.type.NameSpace;
import net.sf.kraken.type.TransportType;
import org.apache.log4j.Logger;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.DelayInformation;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
/**
* Handles incoming events from XMPP server.
*
* @author Daniel Henninger
* @author Mehmet Ecevit
*/
public class XMPPListener implements MessageListener, ConnectionListener, ChatManagerListener, PacketListener, RosterListener {
static Logger Log = Logger.getLogger(XMPPListener.class);
/**
* Creates an XMPP listener instance and ties to session.
*
* @param session Session this listener is associated with.
*/
public XMPPListener(XMPPSession session) {
this.xmppSessionRef = new WeakReference<XMPPSession>(session);
}
/**
* Session instance that the listener is associated with.
*/
public WeakReference<XMPPSession> xmppSessionRef = null;
/**
* Last google mail thread id we saw.
*/
public Long lastGMailThreadId = null;
/**
* Last google mail thread date we saw.
*/
public Date lastGMailThreadDate = null;
/**
* Returns the XMPP session this listener is attached to.
*
* @return XMPP session we are attached to.
*/
public XMPPSession getSession() {
return xmppSessionRef.get();
}
/**
* Handles incoming messages.
*
* @param chat Chat instance this message is associated with.
* @param message Message received.
*/
public void processMessage(Chat chat, org.jivesoftware.smack.packet.Message message) {
Log.debug("Received "+getSession().getTransport().getType().name()+" message: "+message.toXML());
try {
final BaseTransport<XMPPBuddy> transport = getSession().getTransport();
final JID legacyJID = transport.convertIDToJID(message.getFrom());
final JID localJID = getSession().getJID();
final PacketExtension pe = message.getExtension("x", NameSpace.X_DELAY);
final PacketExtension attExt = message.getExtension(AttentionExtension.ELEMENT_NAME, AttentionExtension.NAMESPACE);
if (pe != null && pe instanceof DelayInformation) {
DelayInformation di = (DelayInformation)pe;
transport.sendOfflineMessage(
localJID,
legacyJID,
message.getBody(),
di.getStamp(),
di.getReason()
);
} else if (attExt != null && (attExt instanceof AttentionExtension)) {
transport.sendAttentionNotification(localJID, legacyJID, message.getBody());
}
else {
// see if we got sent chat state notifications
final PacketExtension cse = message
.getExtension("http://jabber.org/protocol/chatstates");
if (cse != null && cse instanceof ChatStateExtension) {
final String chatState = cse.getElementName();
try {
final ChatStateType cst = ChatStateType.valueOf(
ChatStateType.class, chatState);
switch (cst) {
case active:
// only send a 'stand alone' active state if the
// message stanza does not include a text
// message. If it does, the 'active' state is
// included with that chat message below.
if (message.getBody() == null
|| message.getBody().trim().length() == 0) {
transport.sendChatActiveNotification(
localJID, legacyJID);
}
break;
case composing:
transport.sendComposingNotification(localJID,
legacyJID);
break;
case gone:
transport.sendChatGoneNotification(localJID,
legacyJID);
break;
case inactive:
transport.sendChatInactiveNotification(
localJID, legacyJID);
break;
case paused:
transport.sendComposingPausedNotification(
localJID, legacyJID);
break;
default:
Log.debug("Unexpected chat state recieved: " + cst);
break;
}
}
catch (IllegalArgumentException ex) {
Log.warn("Illegal chat state notification "
+ "received from legacy domain: " + chatState);
}
}
if (message.getType() == Type.error) {
Log.debug("Received an error message! Message: " + message.toXML());
transport.sendMessage(localJID, legacyJID, message.getBody(), Message.Type.error);
} else {
transport.sendMessage(localJID, legacyJID, message.getBody());
}
}
// if (message.getProperty("time") == null || message.getProperty("time").equals("")) {
// }
// else {
// getSession().getTransport().sendOfflineMessage(
// getSession().getJID(),
// getSession().getTransport().convertIDToJID(message.getFrom()),
// message.getBody(),
// Message.Type.chat,
// message.getProperty("time").toString()
// );
// }
}
catch (Exception ex) {
Log.debug("E001:"+ ex.getMessage(), ex);
}
}
public void connectionClosed() {
getSession().sessionDisconnectedNoReconnect(null);
}
public void connectionClosedOnError(Exception exception) {
getSession().setFailureStatus(ConnectionFailureReason.UNKNOWN);
getSession().sessionDisconnected(LocaleUtils.getLocalizedString("gateway.xmpp.connectionclosed", "kraken"));
}
public void reconnectingIn(int i) {
//Ignoring for now
}
public void reconnectionSuccessful() {
//Ignoring for now
}
public void reconnectionFailed(Exception exception) {
//Ignoring for now
}
public void chatCreated(Chat chat, boolean b) {
chat.addMessageListener(this);
}
public void processPacket(Packet packet) {
if (packet instanceof GoogleMailBoxPacket) {
if (JiveGlobals.getBooleanProperty("plugin.gateway.gtalk.mailnotifications", true)) {
GoogleMailBoxPacket mbp = (GoogleMailBoxPacket)packet;
this.setLastGMailThreadDate(mbp.getResultTime());
Integer newMailCount = 0;
String mailList = "";
for (GoogleMailThread mail : mbp.getMailThreads()) {
newMailCount++;
if (this.getLastGMailThreadId() == null || mail.getThreadId() > this.getLastGMailThreadId()) {
this.setLastGMailThreadId(mail.getThreadId());
}
String senderList = "";
for (GoogleMailSender sender : mail.getSenders()) {
if (!senderList.equals("")) {
senderList += ", ";
}
String name = sender.getName();
if (name != null) {
senderList += name + " <";
}
senderList += sender.getAddress();
if (name != null) {
senderList += ">";
}
}
mailList += "\n "+senderList+" sent "+mail.getSubject();
}
if (newMailCount > 0) {
getSession().getTransport().sendMessage(
getSession().getJID(),
getSession().getTransport().getJID(),
LocaleUtils.getLocalizedString("gateway.gtalk.mail", "kraken", Arrays.asList(newMailCount))+mailList,
Message.Type.headline
);
}
}
}
else if (packet instanceof IQ) {
Log.debug("XMPP: Got google mail notification");
GoogleNewMailExtension gnme = (GoogleNewMailExtension)packet.getExtension(GoogleNewMailExtension.ELEMENT_NAME, GoogleNewMailExtension.NAMESPACE);
if (gnme != null) {
Log.debug("XMPP: Sending google mail request");
getSession().conn.sendPacket(new IQWithPacketExtension(new GoogleMailNotifyExtension()));
}
}
}
public Long getLastGMailThreadId() {
return lastGMailThreadId;
}
public void setLastGMailThreadId(Long lastGMailThreadId) {
this.lastGMailThreadId = lastGMailThreadId;
}
public Date getLastGMailThreadDate() {
return lastGMailThreadDate;
}
public void setLastGMailThreadDate(Date lastGMailThreadDate) {
this.lastGMailThreadDate = lastGMailThreadDate;
}
public void entriesAdded(Collection<String> addresses) {
for (String addr : addresses) {
RosterEntry entry = getSession().conn.getRoster().getEntry(addr);
getSession().getBuddyManager().storeBuddy(new XMPPBuddy(getSession().getBuddyManager(), entry.getUser(), entry.getName(), entry.getGroups(), entry));
// Facebook does not support presence probes in their XMPP implementation. See http://developers.facebook.com/docs/chat#features
if (!TransportType.facebook.equals(getSession().getTransport().getType())) {
//ProbePacket probe = new ProbePacket(getSession().getJID()+"/"+getSession().xmppResource, entry.getUser());
ProbePacket probe = new ProbePacket(null, entry.getUser());
Log.debug("XMPP: Sending the following probe packet: "+probe.toXML());
try {
getSession().conn.sendPacket(probe);
}
catch (IllegalStateException e) {
Log.debug("XMPP: Not connected while trying to send probe.");
}
}
}
}
public void entriesUpdated(Collection<String> addresses) {
// TODO: Check if we need to do something with this later
}
public void entriesDeleted(Collection<String> addresses) {
for (String addr : addresses) {
getSession().getBuddyManager().removeBuddy(addr);
}
}
public void presenceChanged(org.jivesoftware.smack.packet.Presence presence) {
// Uhm why do we need this given that we have a presence listener
}
}