/*
* 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.msn;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import net.sf.jml.DisplayPictureListener;
import net.sf.jml.MsnContact;
import net.sf.jml.MsnContactPending;
import net.sf.jml.MsnGroup;
import net.sf.jml.MsnList;
import net.sf.jml.MsnMessenger;
import net.sf.jml.MsnObject;
import net.sf.jml.MsnSwitchboard;
import net.sf.jml.event.MsnContactListListener;
import net.sf.jml.event.MsnEmailListener;
import net.sf.jml.event.MsnMessageListener;
import net.sf.jml.event.MsnMessengerListener;
import net.sf.jml.event.MsnSwitchboardListener;
import net.sf.jml.exception.IncorrectPasswordException;
import net.sf.jml.exception.LoginException;
import net.sf.jml.exception.MsgNotSendException;
import net.sf.jml.exception.MsnProtocolException;
import net.sf.jml.exception.UnknownMessageException;
import net.sf.jml.exception.UnsupportedProtocolException;
import net.sf.jml.message.MsnControlMessage;
import net.sf.jml.message.MsnDatacastMessage;
import net.sf.jml.message.MsnEmailActivityMessage;
import net.sf.jml.message.MsnEmailInitEmailData;
import net.sf.jml.message.MsnEmailInitMessage;
import net.sf.jml.message.MsnEmailNotifyMessage;
import net.sf.jml.message.MsnInstantMessage;
import net.sf.jml.message.MsnSystemMessage;
import net.sf.jml.message.MsnUnknownMessage;
import net.sf.jml.message.p2p.DisplayPictureRetrieveWorker;
import net.sf.jml.message.p2p.MsnP2PMessage;
import net.sf.jml.net.Session;
import net.sf.jml.net.SessionListener;
import net.sf.kraken.avatars.Avatar;
import net.sf.kraken.type.ConnectionFailureReason;
import net.sf.kraken.type.TransportLoginStatus;
import net.sf.kraken.util.chatstate.ChatStateEventSource;
import org.apache.log4j.Logger;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.NotFoundException;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Presence;
/**
* MSN Listener Interface.
*
* This handles real interaction with MSN, but mostly is a listener for
* incoming events from MSN.
*
* @author Daniel Henninger
*/
public class MSNListener implements MsnContactListListener, MsnMessageListener, MsnMessengerListener,
MsnSwitchboardListener, MsnEmailListener, SessionListener {
static Logger Log = Logger.getLogger(MSNListener.class);
/**
* Creates the MSN Listener instance.
*
* @param session Session this listener is associated with.
*/
public MSNListener(MSNSession session) {
//this.msnSessionRef = new WeakReference<MSNSession>(session);
this.msnSession = session;
}
/**
* The session this listener is associated with.
*/
//public WeakReference<MSNSession> msnSessionRef = null;
public MSNSession msnSession = null;
/**
* Returns the MSN session this listener is attached to.
*
* @return MSN session we are attached to.
*/
public MSNSession getSession() {
//return msnSessionRef.get();
return msnSession;
}
/**
* Handles incoming messages from MSN users.
*/
public void instantMessageReceived(MsnSwitchboard switchboard, MsnInstantMessage message, MsnContact friend) {
getSession().getTransport().sendMessage(
getSession().getJID(),
getSession().getTransport().convertIDToJID(friend.getEmail().toString()),
message.getContent()
);
// TODO: this will cause a duplicate chat state message to be sent. Prevent this!
final JID to = getSession().getJID();
final JID from = getSession().getTransport().convertIDToJID(friend.getEmail().toString());
getSession().getTransport().getChatStateEventSource().isActive(from, to);
}
/**
* Handles incoming system messages from MSN.
*
* @param messenger Messenger session the message is associated with.
* @param message MSN message.
*/
public void systemMessageReceived(MsnMessenger messenger, MsnSystemMessage message) {
getSession().getTransport().sendMessage(
getSession().getJID(),
getSession().getTransport().getJID(),
message.getContent()
);
}
/**
* Handles incoming control messages from MSN.
*/
public void controlMessageReceived(MsnSwitchboard switchboard, MsnControlMessage message, MsnContact friend) {
if (message.getTypingUser() != null) {
final JID to = getSession().getJID();
final JID from = getSession().getTransport().convertIDToJID(friend.getEmail().toString());
getSession().getTransport().getChatStateEventSource().isComposing(from, to);
}
else {
Log.debug("MSN: Received unknown control msg to " + switchboard + " from " + friend + ": " + message);
}
}
/**
* Handles incoming datacast messages from MSN.
*/
public void datacastMessageReceived(MsnSwitchboard switchboard, MsnDatacastMessage message, MsnContact friend) {
final JID to = getSession().getJID();
final JID from = getSession().getTransport().convertIDToJID(friend.getEmail().toString());
if (message.getId() == 1) {
final String msg = LocaleUtils.getLocalizedString("gateway.msn.nudge", "kraken");
getSession().getTransport().sendAttentionNotification(to, from, msg);
}
else if (message.getId() == 2) {
final String msg = LocaleUtils.getLocalizedString("gateway.msn.wink", "kraken");
getSession().getTransport().sendAttentionNotification(to, from, msg);
}
else {
Log.debug("MSN: Received unknown datacast message to " + switchboard + " from " + friend + ": " + message);
}
}
/**
* Handles incoming unknown messages from MSN.
*/
public void unknownMessageReceived(MsnSwitchboard switchboard, MsnUnknownMessage message, MsnContact friend) {
Log.debug("MSN: Received unknown message to " + switchboard + " from " + friend + ": " + message);
}
public void initialEmailNotificationReceived(MsnSwitchboard switchboard, MsnEmailInitMessage message, MsnContact contact) {
Log.debug("MSN: Got init email notify "+message.getInboxUnread()+" unread message(s)");
if (JiveGlobals.getBooleanProperty("plugin.gateway.msn.mailnotifications", true) && message.getInboxUnread() > 0) {
getSession().getTransport().sendMessage(
getSession().getJID(),
getSession().getTransport().getJID(),
LocaleUtils.getLocalizedString("gateway.msn.initialmail", "kraken", Arrays.asList(message.getInboxUnread())),
Message.Type.headline
);
}
}
public void initialEmailDataReceived(MsnSwitchboard switchboard, MsnEmailInitEmailData message, MsnContact contact) {
Log.debug("MSN: Got init email data "+message.getInboxUnread()+" unread message(s)");
if (JiveGlobals.getBooleanProperty("plugin.gateway.msn.mailnotifications", true) && message.getInboxUnread() > 0) {
getSession().getTransport().sendMessage(
getSession().getJID(),
getSession().getTransport().getJID(),
LocaleUtils.getLocalizedString("gateway.msn.initialmail", "kraken", Arrays.asList(message.getInboxUnread())),
Message.Type.headline
);
}
}
public void newEmailNotificationReceived(MsnSwitchboard switchboard, MsnEmailNotifyMessage message, MsnContact contact) {
Log.debug("MSN: Got new email notification from "+message.getFrom()+" <"+message.getFromAddr()+">");
if (JiveGlobals.getBooleanProperty("plugin.gateway.msn.mailnotifications", true)) {
getSession().getTransport().sendMessage(
getSession().getJID(),
getSession().getTransport().getJID(),
LocaleUtils.getLocalizedString("gateway.msn.mail", "kraken", Arrays.asList(message.getFrom(), message.getFromAddr(), message.getSubject())),
Message.Type.headline
);
}
}
public void activityEmailNotificationReceived(MsnSwitchboard switchboard, MsnEmailActivityMessage message, MsnContact contact) {
Log.debug("MSN: Got email activity notification "+message.getSrcFolder()+" to "+message.getDestFolder());
}
/**
* The user's login has completed and was accepted.
*/
public void loginCompleted(MsnMessenger messenger) {
Log.debug("MSN: Login completed for "+messenger.getOwner().getEmail());
getSession().setLoginStatus(TransportLoginStatus.LOGGED_IN);
}
/**
* Contact list has been synced.
*/
public void contactListSyncCompleted(MsnMessenger messenger) {
Log.debug("MSN: Contact list sync for "+messenger.getOwner().getEmail());
}
/**
* Contact list initialization has completed.
*/
public void contactListInitCompleted(MsnMessenger messenger) {
for (MsnGroup msnGroup : messenger.getContactList().getGroups()) {
Log.debug("MSN: Got group "+msnGroup);
getSession().storeGroup(msnGroup);
}
for (MsnContact msnContact : messenger.getContactList().getContacts()) {
Log.debug("MSN: Got contact "+msnContact);
if (msnContact.isInList(MsnList.FL) && msnContact.getEmail() != null) {
final MSNBuddy buddy = new MSNBuddy(getSession().getBuddyManager(), msnContact);
getSession().getBuddyManager().storeBuddy(buddy);
if (JiveGlobals.getBooleanProperty("plugin.gateway.msn.avatars", true)) {
final MsnObject msnAvatar = msnContact.getAvatar();
if (msnAvatar != null && (buddy.getAvatar() == null || !buddy.getAvatar().getLegacyIdentifier().equals(msnAvatar.getSha1c()))) {
try {
messenger.retrieveDisplayPicture(msnAvatar,
new DisplayPictureListener() {
public void notifyMsnObjectRetrieval(
MsnMessenger messenger,
DisplayPictureRetrieveWorker worker,
MsnObject msnObject,
ResultStatus result,
byte[] resultBytes,
Object context) {
Log.debug("MSN: Got avatar retrieval result: "+result);
// Check for the value
if (result == ResultStatus.GOOD) {
try {
Log.debug("MSN: Found avatar of length "+resultBytes.length);
Avatar avatar = new Avatar(buddy.getJID(), msnAvatar.getSha1c(), resultBytes);
buddy.setAvatar(avatar);
}
catch (IllegalArgumentException e) {
Log.debug("MSN: Got null avatar, ignoring.");
}
}
}
});
}
catch (Exception e) {
Log.debug("MSN: Unable to retrieve MSN avatar: ", e);
}
}
else if (buddy.getAvatar() != null && msnAvatar == null) {
buddy.setAvatar(null);
}
}
}
}
getSession().syncUsers();
}
/**
* A friend for this user has changed status.
*/
public void contactStatusChanged(MsnMessenger messenger, MsnContact friend) {
if (!friend.isInList(MsnList.FL) || friend.getEmail() == null) {
// Not in our buddy list, don't care, or null email address. We need that.
return;
}
if (getSession().getBuddyManager().isActivated()) {
try {
final MSNBuddy buddy = getSession().getBuddyManager().getBuddy(getSession().getTransport().convertIDToJID(friend.getEmail().toString()));
buddy.setPresenceAndStatus(((MSNTransport)getSession().getTransport()).convertMSNStatusToXMPP(friend.getStatus()), friend.getPersonalMessage());
buddy.setMsnContact(friend);
if (JiveGlobals.getBooleanProperty("plugin.gateway.msn.avatars", true)) {
final MsnObject msnAvatar = friend.getAvatar();
if (msnAvatar != null && (buddy.getAvatar() == null || !buddy.getAvatar().getLegacyIdentifier().equals(msnAvatar.getSha1c()))) {
try {
messenger.retrieveDisplayPicture(msnAvatar,
new DisplayPictureListener() {
public void notifyMsnObjectRetrieval(
MsnMessenger messenger,
DisplayPictureRetrieveWorker worker,
MsnObject msnObject,
ResultStatus result,
byte[] resultBytes,
Object context) {
Log.debug("MSN: Got avatar retrieval result: "+result);
// Check for the value
if (result == ResultStatus.GOOD) {
try {
Log.debug("MSN: Found avatar of length "+resultBytes.length);
Avatar avatar = new Avatar(buddy.getJID(), msnAvatar.getSha1c(), resultBytes);
buddy.setAvatar(avatar);
}
catch (IllegalArgumentException e) {
Log.debug("MSN: Got null avatar, ignoring.");
}
}
}
});
}
catch (Exception e) {
Log.debug("MSN: Unable to retrieve MSN avatar: ", e);
}
}
else if (buddy.getAvatar() != null && msnAvatar == null) {
buddy.setAvatar(null);
}
}
}
catch (NotFoundException e) {
// Not in our contact list. Ignore.
Log.debug("MSN: Received presense notification for contact we don't care about: "+friend.getEmail().toString());
}
}
else {
getSession().getBuddyManager().storePendingStatus(getSession().getTransport().convertIDToJID(friend.getEmail().toString()), ((MSNTransport)getSession().getTransport()).convertMSNStatusToXMPP(friend.getStatus()), friend.getPersonalMessage());
}
}
/**
* Someone added us to their contact list.
*/
public void contactAddedMe(MsnMessenger messenger, MsnContact friend) {
Log.debug("MSN: Contact added me: "+ friend.getFriendlyName());
final JID from = getSession().getTransport().convertIDToJID(friend.getEmail().toString());
final Presence p = new Presence();
p.setType(Presence.Type.subscribe);
p.setTo(getSession().getJID());
p.setFrom(from);
getSession().getTransport().sendPacket(p);
}
/**
* Someone removed us from their contact list.
*/
public void contactRemovedMe(MsnMessenger messenger, MsnContact friend) {
Log.debug("MSN: Contact removed me: "+ friend.getFriendlyName());
Presence p = new Presence();
p.setType(Presence.Type.unsubscribe);
p.setTo(getSession().getJID());
p.setFrom(getSession().getTransport().convertIDToJID(friend.getEmail().toString()));
getSession().getTransport().sendPacket(p);
}
/**
* A contact we added has been added to the server.
*/
public void contactAddCompleted(MsnMessenger messenger, MsnContact contact, MsnList list) {
Log.debug("MSN: Contact add completed: "+contact);
// Presence p = new Presence();
// p.setType(Presence.Type.subscribed);
// p.setTo(getSession().getJID());
// p.setFrom(getSession().getTransport().convertIDToJID(contact.getEmail().toString()));
// getSession().getTransport().sendPacket(p);
getSession().completedPendingContactAdd(contact);
}
/**
* A contact we removed has been removed from the server.
*/
public void contactRemoveCompleted(MsnMessenger messenger, MsnContact contact, MsnList list) {
Log.debug("MSN: Contact remove completed: "+contact);
// Presence p = new Presence();
// p.setType(Presence.Type.unsubscribed);
// p.setTo(getSession().getJID());
// p.setFrom(getSession().getTransport().convertIDToJID(contact.getEmail().toString()));
// getSession().getTransport().sendPacket(p);
}
/**
* A group we added has been added to the server.
*/
public void groupAddCompleted(MsnMessenger messenger, MsnGroup group) {
Log.debug("MSN: Group add completed: "+group);
getSession().storeGroup(group);
getSession().completedPendingGroupAdd(group);
}
/**
* A group we removed has been removed from the server.
*/
public void groupRemoveCompleted(MsnMessenger messenger, MsnGroup group) {
Log.debug("MSN: Group remove completed: "+group);
getSession().unstoreGroup(group);
}
/**
* Owner status has changed.
*/
public void ownerStatusChanged(MsnMessenger messenger) {
//getSession().setPresenceAndStatus(((MSNTransport)getSession().getTransport()).convertMSNStatusToXMPP(messenger.getOwner().getStatus()), messenger.getOwner().getPersonalMessage());
}
/**
* Catches MSN exceptions.
*/
public void exceptionCaught(MsnMessenger messenger, Throwable throwable) {
Log.debug("MSN: Exception occurred for "+messenger.getOwner().getEmail()+" : "+throwable);
if (throwable instanceof IncorrectPasswordException) {
getSession().setLoginStatus(TransportLoginStatus.DISCONNECTED);
getSession().setFailureStatus(ConnectionFailureReason.USERNAME_OR_PASSWORD_INCORRECT);
getSession().sessionDisconnectedNoReconnect(LocaleUtils.getLocalizedString("gateway.msn.passwordincorrect", "kraken"));
}
else if (throwable instanceof LoginException) {
// This can be a number of things, but generally it's a failed username and password.
getSession().setLoginStatus(TransportLoginStatus.DISCONNECTED);
getSession().setFailureStatus(ConnectionFailureReason.USERNAME_OR_PASSWORD_INCORRECT);
getSession().sessionDisconnectedNoReconnect(LocaleUtils.getLocalizedString("gateway.msn.passwordincorrect", "kraken"));
}
else if (throwable instanceof MsnProtocolException) {
Log.debug("MSN: Protocol exception: "+throwable.toString());
}
else if (throwable instanceof MsgNotSendException) {
getSession().getTransport().sendMessage(
getSession().getJID(),
getSession().getTransport().getJID(),
LocaleUtils.getLocalizedString("gateway.msn.sendmsgfailed", "kraken")+" "+throwable.toString(),
Message.Type.error
);
}
else if (throwable instanceof UnknownMessageException) {
Log.debug("MSN: Unknown message: "+throwable.toString());
}
else if (throwable instanceof UnsupportedProtocolException) {
Log.debug("MSN: Protocol error: "+throwable.toString());
}
else if (throwable instanceof IOException) {
Log.debug("MSN: IO error: "+throwable.toString());
getSession().setLoginStatus(TransportLoginStatus.DISCONNECTED);
getSession().setFailureStatus(ConnectionFailureReason.CAN_NOT_CONNECT);
getSession().sessionDisconnected(LocaleUtils.getLocalizedString("gateway.msn.disconnect", "kraken"));
}
else {
Log.debug("MSN: Unknown error: "+throwable.toString(), throwable);
}
}
public void contactAddInGroupCompleted(MsnMessenger arg0, MsnContact arg1,
MsnGroup arg2) {
// TODO Auto-generated method stub
}
public void contactAddedMe(MsnMessenger arg0, MsnContactPending[] arg1) {
// TODO Auto-generated method stub
}
public void contactPersonalMessageChanged(MsnMessenger arg0, MsnContact arg1) {
// TODO Auto-generated method stub
}
public void contactRemoveFromGroupCompleted(MsnMessenger arg0,
MsnContact arg1, MsnGroup arg2) {
// TODO Auto-generated method stub
}
public void ownerDisplayNameChanged(MsnMessenger arg0) {
// TODO Auto-generated method stub
}
public void offlineMessageReceived(String arg0, String arg1, String arg2,
MsnContact arg3) {
// TODO Auto-generated method stub
}
public void p2pMessageReceived(MsnSwitchboard arg0, MsnP2PMessage arg1,
MsnContact arg2) {
// TODO Auto-generated method stub
}
public void logout(MsnMessenger arg0) {
// TODO Auto-generated method stub
}
public void contactJoinSwitchboard(MsnSwitchboard arg0, MsnContact arg1) {
// TODO Auto-generated method stub
}
public void contactLeaveSwitchboard(MsnSwitchboard arg0, MsnContact arg1) {
final JID to = getSession().getJID();
final JID from = getSession().getTransport().convertIDToJID(arg1.getEmail().toString());
getSession().getTransport().getChatStateEventSource().isGone(from, to);
}
public void switchboardClosed(MsnSwitchboard arg0) {
// TODO Auto-generated method stub
}
public void switchboardStarted(MsnSwitchboard arg0) {
// TODO Auto-generated method stub
}
public void exceptionCaught(Session arg0, Throwable t) throws Exception{
Log.debug("MSN: Session exceptionCaught for "+getSession().getRegistration().getUsername()+" : "+t);
}
public void messageReceived(Session arg0, net.sf.jml.net.Message message) throws Exception {
Log.debug("MSN: Session messageReceived for "+getSession().getRegistration().getUsername()+" : "+message);
// TODO: Kinda hacky, would like to improve on this later.
if (message.toString().startsWith("OUT OTH")) {
// Forced disconnect because account logged in elsewhere
getSession().setLoginStatus(TransportLoginStatus.DISCONNECTED);
getSession().setFailureStatus(ConnectionFailureReason.LOCKED_OUT);
getSession().sessionDisconnectedNoReconnect(LocaleUtils.getLocalizedString("gateway.msn.otherloggedin", "kraken"));
}
else if (message.toString().startsWith("OUT SDH")) {
// Forced disconnect from server for maintenance
getSession().setLoginStatus(TransportLoginStatus.DISCONNECTED);
getSession().setFailureStatus(ConnectionFailureReason.LOCKED_OUT);
getSession().sessionDisconnectedNoReconnect(LocaleUtils.getLocalizedString("gateway.msn.disconnect", "kraken"));
}
}
public void messageSent(Session arg0, net.sf.jml.net.Message message) throws Exception {
Log.debug("MSN: Session messageSent for "+getSession().getRegistration().getUsername()+" : "+message);
}
public void sessionIdle(Session session) throws Exception {
}
public void sessionEstablished(Session session) {
Log.debug("MSN: Session established for "+getSession().getRegistration().getUsername());
}
public void sessionTimeout(Session session) {
// This is used to handle regular pings to the MSN server. No need to mention it.
}
public void sessionClosed(Session session) {
Log.debug("MSN: Session closed for "+getSession().getRegistration().getUsername());
}
}