/*
* IcqNetWorking.java
*
* Created on 23 ������ 2007 �., 18:57
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
// #sijapp cond.if protocols_ICQ is "true" #
package protocol.icq;
import jimm.comm.Util;
import jimmui.view.timers.*;
import protocol.Protocol;
import protocol.icq.action.ConnectAction;
import protocol.icq.action.IcqAction;
import protocol.net.*;
import protocol.icq.packet.*;
import jimm.*;
/**
*
* @author vladimir
*/
public final class IcqNetWorking extends ClientConnection {
private byte[] flapHeader = new byte[6];
private int nextIcqSequence; // ICQ sequence number counter
private TcpSocket socket;
private Icq icq;
private IcqNetState queue;
private boolean icqConnected = false;
// FLAP sequence number
// Set starting point for seq numbers (not bigger then 0x8000)
private int flapSEQ = Util.nextRandInt() % 0x8000;
private byte[] pingPacket = null;
// Counter variable
private int counter = 0;
// Channel constants
private static final int CHANNEL_CONNECT = 0x01;
private static final int CHANNEL_SNAC = 0x02;
private static final int CHANNEL_ERROR = 0x03;
private static final int CHANNEL_DISCONNECT = 0x04;
private static final int CHANNEL_PING = 0x05;
public IcqNetWorking() {
}
public int getNextCounter() {
return ++counter;
}
public final void processIcqException(JimmException e) {
icq.processException(e);
}
public void initNet(Icq icq) {
this.icq = icq;
queue = new IcqNetState();
queue.login(this);
}
public boolean isIcqConnected() {
return icqConnected;
}
public void setIcqConnected() {
icqConnected = true;
}
public void requestAction(IcqAction act) {
queue.requestAction(act);
}
public Icq getIcq() {
return icq;
}
protected Protocol getProtocol() {
return icq;
}
public final void connectTo(String server) throws JimmException {
// Open connection
if (null != socket) {
socket.close();
}
if (!isConnected()) {
return;
}
socket = new TcpSocket();
socket.connectTo("socket://" + server);
}
// Returns and updates sequence nr
private int getFlapSequence() {
flapSEQ = (++flapSEQ) & 0x7FFF;
return flapSEQ;
}
public void sendPacket(Packet packet) throws JimmException {
if (packet instanceof ToIcqSrvPacket) {
((ToIcqSrvPacket) packet).setIcqSequence(nextIcqSequence++);
}
write(packet.toByteArray());
}
private void write(byte[] out) throws JimmException {
Util.putWordBE(out, 2, getFlapSequence());
socket.write(out);
socket.flush();
}
private void readPacket(TcpSocket socket) throws JimmException {
socket.readFully(flapHeader);
// Verify flap header
if (0x2A != flapHeader[0]) {
throw new JimmException(124, 0);
}
byte[] flapData = new byte[Util.getWordBE(flapHeader, 4)];
socket.readFully(flapData);
Packet packet = parse(Util.getByte(flapHeader, 1), flapData);
flapData = null;
if (null != packet) {
// Get FLAP sequence number
int flapSequence = Util.getWordBE(flapHeader, 2);
queue.processPacket(packet);
}
}
protected void ping() throws JimmException {
if (null != pingPacket) {
write(pingPacket);
}
}
public void initPing() {
pingPacket = new byte[6];
Util.putByte(pingPacket, 0, 0x2a);
Util.putByte(pingPacket, 1, CHANNEL_PING);
Util.putWordBE(pingPacket, 2, 0 /* stub */);
Util.putWordBE(pingPacket, 4, 0);
}
private boolean isShadowNeeded() {
return Jimm.getJimm().phone.isPhone(PhoneInfo.PHONE_NOKIA_S40);
}
protected void connect() throws JimmException {
connect = true;
nextIcqSequence = 0;
if (isShadowNeeded()) {
new GetVersion(GetVersion.TYPE_SHADOW).get();
}
// login
requestAction(new ConnectAction(icq));
queue.processActions();
}
protected boolean processPacket() throws JimmException {
boolean action = queue.processActions();
if ((null != socket) && (0 < socket.available())) {
readPacket(socket);
return true;
}
return action;
}
protected void closeSocket() {
if (null != socket) {
socket.close();
}
}
// Parses given byte array and returns a Packet object
private Packet parse(int channel, byte[] flapData) throws JimmException {
try {
switch (channel) {
case CHANNEL_SNAC:
int family = Util.getWordBE(flapData, 0);
int command = Util.getWordBE(flapData, 2);
if (SnacPacket.OLD_ICQ_FAMILY == family) {
return (SnacPacket.SRV_FROMICQSRV_COMMAND == command)
? FromIcqSrvPacket.parse(flapData) : null;
}
return SnacPacket.parse(family, command, flapData);
case CHANNEL_CONNECT:
return ConnectPacket.parse(flapData);
case CHANNEL_DISCONNECT:
return DisconnectPacket.parse(flapData);
}
} catch (JimmException e) {
throw e;
} catch (Exception e) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
jimm.modules.DebugLog.dump("broken packet " + channel, flapData);
// #sijapp cond.end #
}
return null;
}
public void disconnect() {
icq = null;
IcqNetState l = queue;
queue = null;
if (null != l) {
l.disconnect();
}
connect = false;
}
}
// #sijapp cond.end #