/*
* 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.oscar;
import java.util.Date;
import java.util.List;
import net.kano.joscar.ByteBlock;
import net.kano.joscar.LEBinaryTools;
import net.kano.joscar.OscarTools;
import net.kano.joscar.flap.FlapCommand;
import net.kano.joscar.flap.FlapPacketEvent;
import net.kano.joscar.flapcmd.CloseFlapCmd;
import net.kano.joscar.flapcmd.SnacCommand;
import net.kano.joscar.net.ClientConn;
import net.kano.joscar.net.ClientConnEvent;
import net.kano.joscar.net.ConnDescriptor;
import net.kano.joscar.snac.SnacPacketEvent;
import net.kano.joscar.snac.SnacResponseEvent;
import net.kano.joscar.snaccmd.InfoData;
import net.kano.joscar.snaccmd.conn.MyInfoRequest;
import net.kano.joscar.snaccmd.conn.ServerReadyCmd;
import net.kano.joscar.snaccmd.conn.ServiceRedirect;
import net.kano.joscar.snaccmd.error.SnacError;
import net.kano.joscar.snaccmd.icbm.ParamInfo;
import net.kano.joscar.snaccmd.icbm.ParamInfoCmd;
import net.kano.joscar.snaccmd.icbm.ParamInfoRequest;
import net.kano.joscar.snaccmd.icbm.SetParamInfoCmd;
import net.kano.joscar.snaccmd.icq.MetaShortInfoCmd;
import net.kano.joscar.snaccmd.icq.OfflineMsgDoneCmd;
import net.kano.joscar.snaccmd.icq.OfflineMsgIcqAckCmd;
import net.kano.joscar.snaccmd.icq.OfflineMsgIcqCmd;
import net.kano.joscar.snaccmd.loc.LocRightsCmd;
import net.kano.joscar.snaccmd.loc.LocRightsRequest;
import net.kano.joscar.snaccmd.loc.SetInfoCmd;
import net.kano.joscar.snaccmd.ssi.ActivateSsiCmd;
import net.kano.joscar.snaccmd.ssi.AuthFutureCmd;
import net.kano.joscar.snaccmd.ssi.AuthReplyCmd;
import net.kano.joscar.snaccmd.ssi.BuddyAddedYouCmd;
import net.kano.joscar.snaccmd.ssi.BuddyAuthRequest;
import net.kano.joscar.snaccmd.ssi.ModifyItemsCmd;
import net.kano.joscar.snaccmd.ssi.SsiDataCmd;
import net.kano.joscar.snaccmd.ssi.SsiDataRequest;
import net.kano.joscar.snaccmd.ssi.SsiItem;
import net.kano.joscar.snaccmd.ssi.SsiRightsRequest;
import net.kano.joscar.ssiitem.BuddyItem;
import net.kano.joscar.ssiitem.DefaultSsiItemObjFactory;
import net.kano.joscar.ssiitem.GroupItem;
import net.kano.joscar.ssiitem.IconItem;
import net.kano.joscar.ssiitem.SsiItemObj;
import net.kano.joscar.ssiitem.SsiItemObjectFactory;
import net.kano.joscar.ssiitem.VisibilityItem;
import net.sf.kraken.type.ConnectionFailureReason;
import net.sf.kraken.type.TransportLoginStatus;
import org.apache.log4j.Logger;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.StringUtils;
import org.xmpp.packet.Presence;
/**
* Handles BOS related packets.
*
* @author Daniel Henninger
* Heavily inspired by joscardemo from the joscar project.
*/
public class BOSConnection extends BasicFlapConnection {
static Logger Log = Logger.getLogger(BOSConnection.class);
protected SsiItemObjectFactory itemFactory = new DefaultSsiItemObjFactory();
public BOSConnection(ConnDescriptor cd, OSCARSession mainSession, ByteBlock cookie) {
super(cd, mainSession, cookie); // Hand off to BasicFlapConnection
}
@Override
protected void clientReady() {
super.clientReady();
startKeepAlive();
}
@Override
protected void handleStateChange(ClientConnEvent e) {
Log.debug("OSCAR bos service state change from "+e.getOldState()+" to "+e.getNewState()+" Reason: "+e.getReason()+" User:"+getMainSession().getJID());
// if (e.getNewState() == ClientFlapConn.STATE_NOT_CONNECTED && e.getOldState() == ClientFlapConn.STATE_CONNECTED && getMainSession().isLoggedIn()) {
// getMainSession().sessionDisconnected(LocaleUtils.getLocalizedString("gateway.oscar.disconnected", "kraken"));
// }
// TODO: Evaulate whether we should check reason and triggered reconnect if possible
if (e.getNewState().equals(ClientConn.STATE_NOT_CONNECTED) && e.getReason() != null) {
Log.info ("OSCAR bos disconnected with Reason! call sessionDisconnectedNoReconnect for User:" + getMainSession().getJID());
if (getMainSession()!= null) {
getMainSession().sessionDisconnectedNoReconnect(LocaleUtils.getLocalizedString("gateway.oscar.disconnected", "kraken"));
}
}
}
@Override
protected void handleFlapPacket(FlapPacketEvent e) {
// Log.debug("OSCAR bos flap packet received: "+e);
FlapCommand cmd = e.getFlapCommand();
if (cmd instanceof CloseFlapCmd) {
CloseFlapCmd cfc = (CloseFlapCmd)cmd;
if (cfc.getCode() == CloseFlapCmd.CODE_LOGGED_IN_ELSEWHERE) {
getMainSession().setFailureStatus(ConnectionFailureReason.LOCKED_OUT);
getMainSession().sessionDisconnectedNoReconnect(LocaleUtils.getLocalizedString("gateway.oscar.multilogin", "kraken"));
}
else {
getMainSession().setFailureStatus(ConnectionFailureReason.UNKNOWN);
getMainSession().sessionDisconnected(LocaleUtils.getLocalizedString("gateway.oscar.disconnected", "kraken"));
}
}
super.handleFlapPacket(e);
}
@Override
protected void handleSnacPacket(SnacPacketEvent e) {
// Log.debug("OSCAR bos snac packet received: "+e);
super.handleSnacPacket(e);
SnacCommand cmd = e.getSnacCommand();
if (cmd instanceof ServerReadyCmd) {
request(new ParamInfoRequest());
request(new LocRightsRequest());
request(new SsiRightsRequest());
request(new SsiDataRequest());
}
else if (cmd instanceof BuddyAddedYouCmd) {
BuddyAddedYouCmd bay = (BuddyAddedYouCmd)cmd;
Presence p = new Presence();
p.setType(Presence.Type.subscribe);
p.setTo(getMainSession().getJID());
p.setFrom(getMainSession().getTransport().convertIDToJID(bay.getUin()));
getMainSession().getTransport().sendPacket(p);
}
else if (cmd instanceof BuddyAuthRequest) {
BuddyAuthRequest bar = (BuddyAuthRequest)cmd;
Presence p = new Presence();
p.setType(Presence.Type.subscribe);
p.setTo(getMainSession().getJID());
p.setFrom(getMainSession().getTransport().convertIDToJID(bar.getScreenname()));
getMainSession().getTransport().sendPacket(p);
// Auto-accept auth request. (for now)
// TODO: Evaluate handling this in a non-automated fashion.
request(new AuthReplyCmd(bar.getScreenname(), null, true));
}
else if (cmd instanceof AuthReplyCmd) {
AuthReplyCmd ar = (AuthReplyCmd)cmd;
if (ar.isAccepted()) {
Presence p = new Presence();
p.setType(Presence.Type.subscribed);
p.setTo(getMainSession().getJID());
p.setFrom(getMainSession().getTransport().convertIDToJID(ar.getSender()));
getMainSession().getTransport().sendPacket(p);
}
else {
Presence p = new Presence();
p.setType(Presence.Type.unsubscribed);
p.setTo(getMainSession().getJID());
p.setFrom(getMainSession().getTransport().convertIDToJID(ar.getSender()));
getMainSession().getTransport().sendPacket(p);
}
}
else if (cmd instanceof ModifyItemsCmd) {
ModifyItemsCmd mic = (ModifyItemsCmd)cmd;
List<SsiItem> items = mic.getItems();
for (SsiItem item : items) {
SsiItemObj obj = itemFactory.getItemObj(item);
if (obj instanceof BuddyItem) {
BuddyItem bi = (BuddyItem)obj;
Log.debug("AIM got buddy item " + bi);
getMainSession().getSsiHierarchy().gotBuddy(bi);
}
else if (obj instanceof GroupItem) {
GroupItem gi = (GroupItem)obj;
Log.debug("AIM got group item " + gi);
getMainSession().getSsiHierarchy().gotGroup(gi);
}
}
}
}
@Override
protected void handleSnacResponse(SnacResponseEvent e) {
super.handleSnacResponse(e);
// Log.debug("OSCAR bos snac response received: "+e);
SnacCommand cmd = e.getSnacCommand();
if (cmd instanceof LocRightsCmd) {
request(new SetInfoCmd(new InfoData("oscargateway",
null, getMainSession().getCapabilities(), null)));
request(new MyInfoRequest());
}
else if (cmd instanceof ParamInfoCmd) {
ParamInfoCmd pic = (ParamInfoCmd) cmd;
ParamInfo info = pic.getParamInfo();
request(new SetParamInfoCmd(new ParamInfo(0,
info.getFlags() | ParamInfo.FLAG_TYPING_NOTIFICATION, 8000,
info.getMaxSenderWarning(), info.getMaxReceiverWarning(),
0)));
}
else if (cmd instanceof ServiceRedirect) {
ServiceRedirect sr = (ServiceRedirect) cmd;
getMainSession().connectToService(sr.getSnacFamily(), sr.getRedirectHost(),
sr.getCookie());
}
else if (cmd instanceof SsiDataCmd) {
SsiDataCmd sdc = (SsiDataCmd) cmd;
List<SsiItem> items = sdc.getItems();
for (SsiItem item : items) {
SsiItemObj obj = itemFactory.getItemObj(item);
if (obj instanceof BuddyItem) {
BuddyItem bi = (BuddyItem)obj;
Log.debug("OSCAR: got buddy item " + bi);
getMainSession().getSsiHierarchy().gotBuddy(bi);
}
else if (obj instanceof GroupItem) {
GroupItem gi = (GroupItem)obj;
Log.debug("OSCAR: got group item " + gi);
getMainSession().getSsiHierarchy().gotGroup(gi);
}
else if (obj instanceof IconItem) {
IconItem ii = (IconItem)obj;
Log.debug("OSCAR: got icon item " + ii);
getMainSession().getSsiHierarchy().gotIconItem(ii);
}
else if (obj instanceof VisibilityItem) {
VisibilityItem vi = (VisibilityItem)obj;
Log.debug("OSCAR: got visibility item " + vi);
getMainSession().getSsiHierarchy().gotVisibilityItem(vi);
}
else {
Log.debug("OSCAR: got item we're not handling " + obj);
}
}
if (sdc.getLastModDate() != 0) {
request(new ActivateSsiCmd());
clientReady();
getMainSession().setLoginStatus(TransportLoginStatus.LOGGED_IN);
getMainSession().gotCompleteSSI();
}
}
else if (cmd instanceof OfflineMsgIcqCmd) {
OfflineMsgIcqCmd omic = (OfflineMsgIcqCmd)cmd;
String sn = String.valueOf(omic.getFromUIN());
Date whenSent = omic.getDate();
ByteBlock block = omic.getIcqData();
final int len = LEBinaryTools.getUShort(block, 12) - 1;
String msg = OscarTools.getString(block.subBlock(14, len), null);
msg = StringUtils.unescapeFromXML(OscarTools.stripHtml(msg));
// TODO: Translate offline message note
getMainSession().getTransport().sendOfflineMessage(
getMainSession().getJID(),
getMainSession().getTransport().convertIDToJID(sn),
msg,
whenSent,
"Offline Message"
);
}
else if (cmd instanceof OfflineMsgDoneCmd) {
request(new OfflineMsgIcqAckCmd(getMainSession().getUIN(), (int)getMainSession().nextIcqId()));
}
else if (cmd instanceof MetaShortInfoCmd) {
// MetaShortInfoCmd msic = (MetaShortInfoCmd)cmd;
// Log.debug("RECEIVED META SHORT INFO: "+msic);
// getMainSession().updateRosterNickname(String.valueOf(msic.getUIN()), msic.getNickname());
}
else if (cmd instanceof AuthReplyCmd) {
AuthReplyCmd ar = (AuthReplyCmd)cmd;
if (ar.isAccepted()) {
Presence p = new Presence();
p.setType(Presence.Type.subscribed);
p.setTo(getMainSession().getJID());
p.setFrom(getMainSession().getTransport().convertIDToJID(ar.getSender()));
getMainSession().getTransport().sendPacket(p);
}
else {
Presence p = new Presence();
p.setType(Presence.Type.unsubscribed);
p.setTo(getMainSession().getJID());
p.setFrom(getMainSession().getTransport().convertIDToJID(ar.getSender()));
getMainSession().getTransport().sendPacket(p);
}
}
else if (cmd instanceof AuthFutureCmd) {
AuthFutureCmd af = (AuthFutureCmd)cmd;
Presence p = new Presence();
p.setType(Presence.Type.subscribe);
p.setTo(getMainSession().getJID());
p.setFrom(getMainSession().getTransport().convertIDToJID(af.getUin()));
getMainSession().getTransport().sendPacket(p);
}
else if (cmd instanceof SnacError) {
SnacError se = (SnacError)cmd;
if (se.getErrorCode() == SnacError.CODE_REFUSED_BY_CLIENT) {
getMainSession().getTransport().sendMessage(
getMainSession().getJID(),
getMainSession().getTransport().getJID(),
LocaleUtils.getLocalizedString("gateway.aim.msgrefused","kraken")
);
}
//TODO: Tons more errors that can be caught. Gotta catch 'em all! =) (please don't sue me Nintendo)
}
}
}