/*
* ClientDetector.java
*
* Created on 19 Октябрь 2009 г., 19:42
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
// #sijapp cond.if protocols_ICQ is "true" #
// #sijapp cond.if modules_CLIENTS is "true" #
package protocol.icq;
import jimmui.view.icons.*;
import java.io.*;
import jimm.comm.*;
import protocol.ui.ClientInfo;
import protocol.net.TcpSocket;
/**
*
* @author Vladimir Krukov
*/
public class ClientDetector {
/** Creates a new instance of ClientDetector */
private boolean unloaded = true;
private String[] clients;
private short[] iconIndex;
private byte[] code;
private int[] dataFp;
private byte[] dataGuid;
private final ImageList clientIcons = ImageList.createImageList("/clients.png");
public static final ClientDetector instance = new ClientDetector();
public boolean has(int id) {
return !unloaded && (0 <= id) && (id < clients.length);
}
public ClientInfo get() {
return new ClientInfo(clientIcons, iconIndex, clients);
}
private short[] readBytes(DataInputStream in, int size) throws IOException {
short[] array = new short[size];
for (int i = 0; i < size; ++i) {
array[i] = (short)in.readUnsignedByte();
}
return array;
}
public ClientDetector() {
InputStream stream = null;
DataInputStream is = null;
try {
stream = jimm.Jimm.getResourceAsStream("/clients.bin");
is = new DataInputStream(stream);
code = new byte[is.readInt()];
is.readFully(code);
dataGuid = new byte[is.readInt()];
is.readFully(dataGuid);
dataFp = new int[is.readInt()];
for (int i = 0; i < dataFp.length; ++i) {
dataFp[i] = is.readInt();
}
clients = new String[is.readUnsignedByte()];
for (int i = 0; i < clients.length; ++i) {
clients[i] = is.readUTF();
}
iconIndex = readBytes(is, clients.length);
for (int i = 0; i < iconIndex.length; ++i) {
iconIndex[i]--;
}
unloaded = false;
} catch (Exception e) {
unloaded = true;
code = null;
dataGuid = null;
dataFp = null;
clients = null;
code = new byte[0];
clients = new String[]{"None"};
}
TcpSocket.close(stream);
TcpSocket.close(is);
// #sijapp cond.if modules_DEBUGLOG is "true" #
_g();
// #sijapp cond.end #
}
private int getByte(byte[] data, int offset) {
return ((int)data[offset]) & 0xFF;
}
private int getWord(byte[] data, int offset) {
return (data[offset + 1] & 0xFF) | ((data[offset + 0] & 0xFF) << 8);
}
private byte[] getGuid(byte[] buf, int offset) {
byte[] guid = new byte[16];
System.arraycopy(buf, offset, guid, 0, 16);
return guid;
}
private int findGuid(byte[] guids, int ip) {
final int packed = getByte(code, ip++);
//int offset = packed >> 4;
final int length = packed;
// #sijapp cond.if modules_DEBUGLOG is "true" #
jimm.modules.DebugLog.assert0("length is 0", (0 == length));
// #sijapp cond.end #
final int where = getWord(code, ip);
int byteIndex = 0;
for (int guidNum = 0; guidNum < guids.length; guidNum += 16) {
if (guids[guidNum] == dataGuid[where]) {
for (byteIndex = 0; byteIndex < length; ++byteIndex) {
if (guids[guidNum + byteIndex] != dataGuid[where + byteIndex]) {
break;
}
}
if (length == byteIndex) {
return guidNum;
}
}
}
return -1;
}
/**
* OpCode bits:
* 0 - caps count (capsCount(1b))
* 1 - fp0 (fpIndex(1b))
* 2 - fp1 (fpIndex(1b))
* 3 - fp2 (fpIndex(1b))
* 4 - caps (count(1b) (length(1b) where(2b))+)
* 5 - !caps (count(1b) (length(1b) where(2b))+)
* 6 - Version (type(1b)=
* 1 - GUID string (length(1b) where(2b)) (offset length)(1b))
* 2 - GUID bytes (length(1b) where(2b)) (offset length)(1b))
* 3 - GUID Miranda (length(1b) where(2b)) (offset=0 length=0)(1b))
* 4 - FP bytes (fpIndex(1b))
* 5 - FP string (fpIndex(1b))
* 6 - FP int (fpIndex(1b))
*
* 6 - Version (type(1b=0) (length(1b) where(2b)) (offset length)(1b))
* 6 - Version (type(1b=...) (...)
* 7 - protocol (protocol(2b))
*/
private boolean execVMProc(IcqContact contact, byte[] guids, int[] fps, int protocol, int ip) {
byte opCode = code[ip++];
if (0 != (opCode & 0x80)) {
final int proto = getWord(code, ip);
if (protocol != proto) {
return false;
}
ip += 2;
}
if (0 != (opCode & 0x01)) {
if ((guids.length / 16) != code[ip++]) {
return false;
}
}
if (0 != (opCode & 0x02)) {
if (fps[0] != dataFp[getByte(code, ip++)]) {
return false;
}
}
if (0 != (opCode & 0x04)) {
if (fps[1] != dataFp[getByte(code, ip++)]) {
return false;
}
}
if (0 != (opCode & 0x08)) {
if (fps[2] != dataFp[getByte(code, ip++)]) {
return false;
}
}
// Caps is contained
if (0 != (opCode & 0x10)) {
int guidsCount = code[ip++];
for (int i = 0 ; i < guidsCount; ++i) {
if (-1 == findGuid(guids, ip)) {
return false;
}
ip += 3;
}
}
// Caps isn't contained
if (0 != (opCode & 0x20)) {
int guidsCount = code[ip++];
for (int i = 0 ; i < guidsCount; ++i) {
if (-1 != findGuid(guids, ip)) {
return false;
}
ip += 3;
}
}
String version = null;
if (0 != (opCode & 0x40)) {
int versionType = code[ip++];
switch (versionType) {
case 0:
case 1:
case 2:
int versionGuid = findGuid(guids, ip);
if (-1 == versionGuid) {
return false;
}
ip += 3;
int versionOffset = ((int)code[ip] >> 4) & 0x0F;
int versionLength = ((int)code[ip]) & 0x0F;
++ip;
version = getGuidVersion(guids, versionGuid, versionOffset, versionLength, versionType);
break;
case 3:
case 4:
case 5:
version = getFpVersion(fps[getByte(code, ip++)], versionType - 3);
break;
}
}
//println(ip + ": " + clients[code[ip]]);
System.out.print("client " + getByte(code, ip));
contact.setClient((short)getByte(code, ip), version);
return true;
}
private String getGuidVersion(byte[] guids, int guidOffset, int offset, int length, int versionType) {
if (0 == versionType) {
return StringUtils.byteArrayToString(guids, guidOffset + offset, length).trim();
}
if (1 == versionType) {
StringBuilder version = new StringBuilder();
offset += guidOffset;
for (int i = 0; i < length; ++i) {
if (0 < i) version.append('.');
version.append(guids[offset + i] | 0xFF);
}
return version.toString();
}
byte[] buf = getGuid(guids, guidOffset);
byte first = buf[0];
if (('i' == first) || ('s' == first) || ('e' == first)) {
String version = makeVersion(buf[0x4] & 0x7F, buf[0x5], buf[0x6], buf[0x7]);
if ((buf[0x4] & 0x80) != 0) {
version += "a";
}
return version;
}
if (('M' == buf[0]) && ('i' == buf[1])) {
if ( (buf[0xC] == 0) && (buf[0xD] == 0) && (buf[0xE] == 0) && (buf[0xF] == 1) ) {
return "0.1.2.0";
} else if ((buf[0xC] == 0) && (buf[0xD] <= 3) && (buf[0xE] <= 3) && (buf[0xF] <= 1)) {
return makeVersion(0, buf[0xD], buf[0xE], buf[0xF]);
} else {
String ver = makeVersion(buf[0x8] & 0x7F, buf[0x9], buf[0xA], buf[0xB]);
if ((buf[0x8] & 0x80) != 0) {
ver += "a";
}
return ver;
}
}
String version = makeVersion(buf[offset + 0x0] & 0x7F, buf[offset + 0x1],
buf[offset + 0x2], buf[offset + 0x3]);
if ((buf[offset + 0x0] & 0x80) != 0) {
version += "a";
}
return version;
}
private String getFpVersion(int fp, int versionType) {
switch (versionType) {
case 0: return makeVersion(getByte(fp, 24), getByte(fp, 16), getByte(fp, 8), getByte(fp, 0));
case 1: return "" + getByte(fp, 24) + getByte(fp, 16) + getByte(fp, 8) + getByte(fp, 0);
case 2: return String.valueOf(fp);
}
return null;
}
private String makeVersion(int v0, int v1, int v2, int v3) {
String ver = (v0 | 0xFF) + "." + (v1 | 0xFF);
if (0 <= v2 || 0 <= v3) {
ver += "." + (v2 | 0xFF);
if (0 <= v3) {
ver += "." + (v3 | 0xFF);
}
}
return ver;
}
private int getByte(int val, int index) {
return ((val >> index) & 0xFF);
}
// #sijapp cond.if modules_DEBUGLOG is "true" #
private void println(String s) {
//System.out.println(s);
}
private void _g() {
int ip_ = 0;
int ip = 0;
try {
byte[] _code = code;
while (ip_ < _code.length) {
int length = getByte(_code, ip_);
byte[] cli = new byte[length];
System.arraycopy(_code, ip_ + 1, cli, 0, length);
ip_ += length + 1;
ip = 0;
byte opCode = cli[ip++];
if (0 != (opCode & 0x80)) {
println("protocol " + getWord(cli, ip));
ip += 2;
}
if (0 != (opCode & 0x01)) {
println("guid count " + cli[ip++]);
}
if (0 != (opCode & 0x02)) {
println("FP1 " + dataFp[getByte(cli, ip++)]);
}
if (0 != (opCode & 0x04)) {
println("FP2 " + dataFp[getByte(cli, ip++)]);
}
if (0 != (opCode & 0x08)) {
println("FP3 " + dataFp[getByte(cli, ip++)]);
}
// Caps is contained
if (0 != (opCode & 0x10)) {
int guidsCount = cli[ip++];
println("contains " + guidsCount);
for (int i = 0 ; i < guidsCount; ++i) {
ip += 3;
}
}
// Caps isn't contained
if (0 != (opCode & 0x20)) {
int guidsCount = cli[ip++];
println("uncontains " + guidsCount);
for (int i = 0 ; i < guidsCount; ++i) {
ip += 3;
}
}
String version = null;
if (0 != (opCode & 0x40)) {
int versionType = cli[ip++];
switch (versionType) {
case 0:
case 1:
case 2:
println("guid ver");
final int length_ = getByte(cli, ip++);
final int where_ = getWord(cli, ip);
ip += 2;
byte[] dddddddd = new byte[length_];
System.arraycopy(dataGuid, where_, dddddddd, 0, length_);
ip++;
break;
case 3:
case 4:
case 5:
ip++;
println("fp ver");
break;
}
}
println("type " + getByte(cli, ip));
println("client " + clients[getByte(cli, ip)]);
}
} catch (Exception e) {
println("type " + ip_ + ":" + ip);
}
}
// #sijapp cond.end #
public void execVM(IcqContact contact, byte[] guids, int[] fps, int protocol) {
contact.setClient(ClientInfo.CLI_NONE, null);
if (unloaded) return;
try {
//jimm.modules.DebugLog.dump("caps", guids);
//jimm.modules.DebugLog.println("fps " + fps[0] + ", " + fps[1] + ", " + fps[2]);
//jimm.modules.DebugLog.println("protocol " + protocol);
int ip = 0;
while (ip < code.length) {
if (execVMProc(contact, guids, fps, protocol, ip + 1)) break;
ip = ip + (code[ip] & 0xFF) + 1;
}
} catch (Exception ignored) {
}
}
}
// #sijapp cond.end #
// #sijapp cond.end #