/*******************************************************************************
Jimm - Mobile Messaging - J2ME ICQ clone
Copyright (C) 2003-06 Jimm Project
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************
File: src/jimm/comm/ActionListener.java
Version: ###VERSION### Date: ###DATE###
Author(s): Manuel Linsmayer, Andreas Rossbacher, Spassky Alexander, Igor Palkin
*******************************************************************************/
// #sijapp cond.if protocols_ICQ is "true" #
package protocol.icq;
import jimm.*;
import jimm.chat.message.*;
import jimm.comm.*;
import protocol.*;
import protocol.icq.packet.*;
import protocol.icq.plugin.*;
import jimm.modules.*;
import jimm.util.JLocale;
import protocol.ui.XStatusInfo;
public final class IcqNetDefActions {
public static final int FLAG_HAPPY = 0x0008;
// public static final int FLAG_WEBAWARE = 0x0001;
private IcqNetWorking connection;
public IcqNetDefActions(IcqNetWorking net) {
connection = net;
}
private Icq getIcq() {
return connection.getIcq();
}
private void sendPacket(Packet packet) throws JimmException {
connection.sendPacket(packet);
}
private void updateMessageStatus(SnacPacket packet) throws JimmException {
long msgId = packet.getReader().getDWordBE() & 0xFFFFFFFFL;
boolean toClient = (packet.getCommand() != 0x000C);
int notifyType = toClient ? PlainMessage.NOTIFY_FROM_CLIENT : PlainMessage.NOTIFY_FROM_SERVER;
connection.markMessageSent(msgId, notifyType);
}
/** ************************************************************************* */
private void processOfflineMessage(ArrayReader reader) {
// Extract UIN
long uinRaw = reader.getDWordLE();
String uin = String.valueOf(uinRaw);
int year = reader.getWordLE();
int mon = reader.getByte();
int day = reader.getByte();
int hour = reader.getByte();
int min = reader.getByte();
int sec = 0;
// Get type
int type = reader.getWordLE();
// Get text length
int textLen = reader.getWordLE();
byte[] msgData = reader.getArray(textLen);
String text = StringUtils.byteArrayToString(msgData, 0, msgData.length);
if (StringUtils.isEmpty(text)) {
return;
}
// Extract date of dispatch
long date = Util.createGmtTime(year, mon, day, hour, min, sec) + 5 * 60 * 60;
if (0x0001 == type) { // Normal message
addOfflineMessage(uin, text, date);
}
}
// Forwards received packet
public void forward(Packet packet) throws JimmException {
// Watch out for channel 4 (Disconnect) packets
if (packet instanceof DisconnectPacket) {
DisconnectPacket disconnectPacket = (DisconnectPacket) packet;
// Throw exception
throw disconnectPacket.makeException();
}
if (packet instanceof FromIcqSrvPacket) {
FromIcqSrvPacket fromIcqSrvPacket = (FromIcqSrvPacket) packet;
// Watch out for SRV_OFFLINEMSG
if (FromIcqSrvPacket.SRV_OFFLINEMSG_SUBCMD == fromIcqSrvPacket.getSubcommand()) {
processOfflineMessage(fromIcqSrvPacket.getReader());
// Watch out for SRV_DONEOFFLINEMSGS
} else if (FromIcqSrvPacket.SRV_DONEOFFLINEMSGS_SUBCMD == fromIcqSrvPacket.getSubcommand()) {
sendPacket(new ToIcqSrvPacket(0x00000000, getIcq().getUserId(),
ToIcqSrvPacket.CLI_ACKOFFLINEMSGS_SUBCMD, new byte[0], new byte[0]));
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else {
unknownPacket(packet);
// #sijapp cond.end#
}
} else if (packet instanceof SnacPacket) {
SnacPacket snacPacket = (SnacPacket) packet;
int family = snacPacket.getFamily();
int command = snacPacket.getCommand();
if (SnacPacket.CONTACT_FAMILY == family) {
if (SnacPacket.SRV_USERONLINE_COMMAND == command) {
userOnline(snacPacket);
} else if (SnacPacket.SRV_USEROFFLINE_COMMAND == command) {
userOffline(snacPacket);
} else if (0x1F == command) {
// #sijapp cond.if modules_MAGIC_EYE is "true" #
MagicEye.addAction(getIcq(), "1", "Verification");
// #sijapp cond.end #
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.println("Verification");
// #sijapp cond.end #
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else {
unknownPacket(packet);
// #sijapp cond.end#
}
} else if (SnacPacket.CLI_ICBM_FAMILY == family) {
if ((SnacPacket.SRV_MSG_ACK_COMMAND == command)
|| (SnacPacket.CLI_ACKMSG_COMMAND == command)) {
updateMessageStatus(snacPacket);
}
if (SnacPacket.CLI_ACKMSG_COMMAND == command) {
ackMessage(snacPacket.getReader());
} else if (SnacPacket.SRV_RECVMSG_COMMAND == command) {
try {
readMessage(snacPacket.getReader());
} catch (Exception ignored) {
}
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else if (SnacPacket.SRV_MISSED_MESSAGE_COMMAND == command) {
ArrayReader marker = snacPacket.getReader();
marker.skip(2);
int uinLen = marker.getByte();
String uin = StringUtils.byteArrayToAsciiString(
marker.getArray(uinLen), 0, uinLen);
marker.skip(2 /* warning level */);
int tlvCount = marker.getWordBE();
for (int i = 0; i < tlvCount; ++i) {
marker.skipTlv();
}
int count = marker.getWordBE();
int reasone = marker.getWordBE();
DebugLog.println("msg: " + uin + " [" + reasone + "]x("+count+")");
// #sijapp cond.end#
// #sijapp cond.if modules_SOUND is "true" #
} else if (SnacPacket.SRV_MTN_COMMAND == command) {
// Typing notify
ArrayReader buf = snacPacket.getReader();
buf.skip(10);
int uin_len = buf.getByte();
String uin = StringUtils.byteArrayToAsciiString(
buf.getArray(uin_len), 0, uin_len);
int flag = buf.getWordBE();
if (Options.getInt(Options.OPTION_TYPING_MODE) > 0) {
getIcq().beginTyping(uin, (0x0002 == flag));
}
// #sijapp cond.end#
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else {
unknownPacket(packet);
// #sijapp cond.end#
}
// Watch out for SRV_ADDEDYOU
} else if (SnacPacket.SSI_FAMILY == family) {
if (SnacPacket.SRV_ADDEDYOU_COMMAND == command) {
// Get UIN of the contact changing status
String uin = getUinByByteLen(snacPacket.getReader());
// #sijapp cond.if modules_MAGIC_EYE is "true" #
MagicEye.addAction(getIcq(), uin,
JLocale.getString("youwereadded") + uin);
// #sijapp cond.end #
// Watch out for SRV_GRAND
} else if (SnacPacket.SRV_GRANT_FUTURE_AUTH_COMMAND == command) {
ArrayReader authMarker = snacPacket.getReader();
// Get UIN of the contact changing status
int length = authMarker.getByte();
String uin = StringUtils.byteArrayToAsciiString(
authMarker.getArray(length), 0, length);
String reason = getReasone(authMarker);
getIcq().setAuthResult(uin, true);
// Watch out for SRV_AUTHREQ
} else if (SnacPacket.SRV_AUTHREQ_COMMAND == command) {
ArrayReader authMarker = snacPacket.getReader();
// Get UIN of the contact changing status
int length = authMarker.getByte();
String uin = StringUtils.byteArrayToAsciiString(
authMarker.getArray(length), 0, length);
String reason = getReasone(authMarker);
getIcq().addMessage(new SystemNotice(getIcq(), SystemNotice.TYPE_NOTICE_AUTHREQ, uin, reason));
// Watch out for SRV_AUTHREPLY
} else if (SnacPacket.SRV_AUTHREPLY_COMMAND == command) {
ArrayReader authMarker = snacPacket.getReader();
// Get UIN of the contact changing status
int length = authMarker.getByte();
String uin = StringUtils.byteArrayToAsciiString(
authMarker.getArray(length), 0, length);
// Get granted boolean
boolean granted = (0x01 == authMarker.getByte());
String reason = getReasone(authMarker);
// Handle the new system notice
if (granted) {
getIcq().setAuthResult(uin, true);
} else {
Contact c = getIcq().getItemByUID(uin);
if ((null != c) && !c.isTemp()) {
// #sijapp cond.if modules_MAGIC_EYE is "true" #
MagicEye.addAction(getIcq(), uin,
JLocale.getString("denyedby") + uin);
// #sijapp cond.end #
}
}
} else if (SnacPacket.CLI_ROSTERDELETE_COMMAND == command) {
ArrayReader buf = snacPacket.getReader();
int length = buf.getWordBE();
String uin = StringUtils.byteArrayToAsciiString(
buf.getArray(length), 0, length);
buf.skip(4);
int type = buf.getWordBE();
if (0 == type) {
Contact c = getIcq().getItemByUID(uin);
if ((null != c) && !c.isTemp()) {
c.setTempFlag(true);
String message = JLocale.getString("contact_has_been_removed");
if (c.hasChat()) {
getIcq().addMessage(new SystemNotice(getIcq(),
SystemNotice.TYPE_NOTICE_MESSAGE, uin, message));
}
// #sijapp cond.if modules_MAGIC_EYE is "true" #
MagicEye.addAction(getIcq(), uin, message);
// #sijapp cond.end #
}
}
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else {
unknownPacket(packet);
// #sijapp cond.end#
}
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else {
unknownPacket(packet);
// #sijapp cond.end#
}
}
}
private String getReasone(ArrayReader marker) {
String reason = null;
try {
int length = marker.getWordBE();
reason = StringUtils.byteArrayToString(
marker.getArray(length), 0, length);
} catch (Exception ignored) {
}
return reason;
}
// Merge two received capabilities into one byte array
private byte[] mergeCapabilities(byte[] oldCaps, byte[] newCaps) {
if (null == newCaps) {
return oldCaps;
}
// Extend new capabilities to match with old ones
int newCapsCount = 0;
for (int i = 0; i < newCaps.length; i += 2) {
if (0x13 != newCaps[i]) continue;
newCapsCount++;
}
if (0 == newCapsCount) {
return oldCaps;
}
final int oldCapsSize = (null == oldCaps) ? 0 : oldCaps.length;
final byte[] allCaps = new byte[newCapsCount * 16 + oldCapsSize];
int allCapsPos = 0;
if (0 < oldCapsSize) {
System.arraycopy(oldCaps, 0, allCaps, allCapsPos, oldCapsSize);
allCapsPos += oldCapsSize;
}
final byte[] CAP_OLD = GUID.CAP_DC.toByteArray();
for (int i = 0; i < newCaps.length; i += 2) {
if (0x13 != newCaps[i]) continue;
System.arraycopy(CAP_OLD, 0, allCaps, allCapsPos, CAP_OLD.length);
System.arraycopy(newCaps, i, allCaps, allCapsPos + 2, 2);
allCapsPos += 16;
}
return allCaps;
}
// #sijapp cond.if modules_DEBUGLOG is "true" #
private void unknownPacket(Packet packet) {
if (!(packet instanceof SnacPacket)) {
return;
}
SnacPacket snacPacket = (SnacPacket) packet;
int family = snacPacket.getFamily();
int command = snacPacket.getCommand();
if (0x1 == family) {
if (0x21 == command) return;// my iMessage
if (0x0F == command) return;// my status info
// if (0x0A == command) {
// DebugLog.dump("0x01 0x0A", snacPacket.getData());
// }
}
if (packet instanceof FromIcqSrvPacket) {
FromIcqSrvPacket fisp = (FromIcqSrvPacket)packet;
ArrayReader reader = fisp.getReader();
int cmd = reader.getWordLE();
DebugLog.println("pkt: family 0x" + Integer.toHexString(family)
+ " command 0x" + Integer.toHexString(command)
+ " type 0x" + Integer.toHexString(fisp.getSubcommand())
+ " subtype 0x" + Integer.toHexString(cmd)
+ " length=" + snacPacket.getReader().getBuffer().length);
} else {
DebugLog.println("pkt: family 0x" + Integer.toHexString(family)
+ " command 0x" + Integer.toHexString(command)
+ " length=" + snacPacket.getReader().getBuffer().length);
}
}
// #sijapp cond.end#
private void ackMessage(ArrayReader reader) throws JimmException {
reader.skip(10); // cookie (8), chanel (2)
int uinLen = reader.getByte() /* userid length */;
String uin = StringUtils.byteArrayToAsciiString(
reader.getArray(uinLen), 0, uinLen);
reader.skip(2); // reasone (2)
// Get message type
if ((58 + uinLen) < reader.getBuffer().length) {
reader.setOffset(58 + uinLen);
int msgType = reader.getWordLE();
reader.setOffset(64 + uinLen);
int textLen = reader.getWordLE();
reader.skip(textLen);
if (MESSAGE_TYPE_EXTENDED == msgType) {
// #sijapp cond.if modules_XSTATUSES is "true" #
XtrazMessagePlugin plg = unpackPlugin(reader, uin);
if (null != plg) {
sendPacket(plg.getPacket());
}
// #sijapp cond.end #
}
}
}
private String removeHtml(String text) {
if (!text.startsWith("<HTML>")) {
return text;
}
StringBuilder escapedText = new StringBuilder();
boolean inTag = false;
for (int i = 0; i < text.length(); ++i) {
char ch = text.charAt(i);
if (inTag) {
if ('>' == ch) {
inTag = false;
}
} else {
if ('<' == ch) {
inTag = true;
if (text.startsWith("br", i + 1) || text.startsWith("BR", i + 1)) {
escapedText.append("\n");
}
} else {
escapedText.append(ch);
}
}
}
return Util.xmlUnescape(escapedText.toString());
}
private boolean isMrim(String userId) {
return -1 != userId.indexOf('@');
}
private void addMessage(String uin, String text) {
if (StringUtils.isEmpty(text)) {
return;
}
if (isMrim(uin)) {
// FIXME: it's hack
text = Util.xmlUnescape(text);
text = StringUtils.convert(StringUtils.MRIM2JIMM, text);
}
text = removeHtml(text);
text = StringUtils.removeCr(text);
text = StringUtils.trim(text);
if (StringUtils.isEmpty(text)) {
return;
}
getIcq().addMessage(new PlainMessage(uin, getIcq(),
Jimm.getCurrentGmtTime(), text, false));
}
private void addOfflineMessage(String uin, String text, long date) {
if (StringUtils.isEmpty(text)) {
return;
}
text = StringUtils.removeCr(text);
text = StringUtils.trim(text);
if (StringUtils.isEmpty(text)) {
return;
}
getIcq().addMessage(new PlainMessage(uin, getIcq(), date, text, true));
}
void sendRecivedFlag(byte[] buf, String uin, boolean msg2Buf) throws JimmException {
// Acknowledge message
OutStream packet = new OutStream();
packet.writeByteArray(buf, 0, 8);
packet.writeWordBE(0x0002);
packet.writeShortLenAndUtf8String(uin);
packet.writeWordBE(0x0003);
packet.writeWordLE(0x001b);
packet.writeWordLE(0x0008);
packet.writeDWordLE(0);
packet.writeDWordLE(0);
packet.writeDWordLE(0);
packet.writeDWordLE(0);
packet.writeWordLE(0x0000);
packet.writeDWordLE(0x00000003);
packet.writeByte(0x00);
packet.writeWordLE(0xFFFE);
packet.writeWordLE(0x000E);
packet.writeWordLE(0xFFFE);
packet.writeDWordLE(0);
packet.writeDWordLE(0);
packet.writeDWordLE(0);
packet.writeDWordLE(1);
packet.writeWordLE(1);
packet.writeWordLE(0x0001);
packet.writeByte(0x00);
sendPacket(new SnacPacket(SnacPacket.CLI_ICBM_FAMILY,
SnacPacket.CLI_ACKMSG_COMMAND, 0, packet.toByteArray()));
}
private void readMessage(ArrayReader marker) throws JimmException {
// Get message format
marker.skip(8 /* cookie */);
int format = marker.getWordBE();
// Get UIN length
int uinLen = marker.getByte();
// Get UIN
String uin = StringUtils.byteArrayToAsciiString(
marker.getArray(uinLen), 0, uinLen);
marker.skip(2 /* WARNING */);
// Skip TLVS
int tlvCount = marker.getWordBE();
for (int i = 0; i < tlvCount; ++i) {
marker.skipTlv();
}
// Get message data and initialize marker
int tlvType;
byte[] msgBuf;
do {
tlvType = marker.getTlvType();
msgBuf = marker.getTlv();
} while ((tlvType != 0x0002) && (tlvType != 0x0005));
ArrayReader msgMarker = new ArrayReader(msgBuf, 0);
//////////////////////
// Message format 1 //
//////////////////////
if (format == 0x0001) {
// Variables for all possible TLVs
// byte[] capabilities = null;
byte[] message = null;
// Read all TLVs
while (msgMarker.isNotEnd()) {
// Get type of next TLV
tlvType = msgMarker.getTlvType();
// Get next TLV
byte[] tlvValue = msgMarker.getTlv();
// Save value
switch (tlvType) {
case 0x0501:
// capabilities
// capabilities = tlvValue;
break;
case 0x0101:
// message
message = tlvValue;
break;
}
}
// Process packet if at least the message TLV was present
if (null != message) {
boolean ucs2 = (0x0002 == Util.getWordBE(message, 0));
// Get message text
String text = null;
if (ucs2) {
text = StringUtils.ucs2beByteArrayToString(message, 4, message.length - 4);
} else {
text = StringUtils.byteArrayToWinString(message, 4, message.length - 4);
}
// Construct object which encapsulates the received
// plain message
addMessage(uin, text);
sendRecivedFlag(marker.getBuffer(), uin, false);
}
//////////////////////
// Message format 2 //
//////////////////////
} else if (format == 0x0002) {
// TLV(A): Acktype 0x0000 - normal message
// 0x0001 - file request / abort request
// 0x0002 - file ack
// Get and validate SUB_MSG_TYPE2.COMMAND
int cmd = msgMarker.getWordBE();
if (0x0000 != cmd) return; // Only normal messages are supported yet
// Skip SUB_MSG_TYPE2.TIME and SUB_MSG_TYPE2.ID
msgMarker.skip(4 + 4);
// Skip SUB_MSG_TYPE2.CAPABILITY
msgMarker.skip(16);
// #sijapp cond.if modules_FILES is "true"#
int ackType = -1;
byte[] extIP = new byte[4];
byte[] ip = new byte[4];
int port = 0;
// #sijapp cond.end#
int status = -1;
// Get message data and initialize marker
byte[] msg2Buf = null;
do {
// Get type of next TLV
tlvType = msgMarker.getTlvType();
// Get next TLV
byte[] infoBuf = msgMarker.getTlv();
// #sijapp cond.if modules_FILES is "true"#
switch (tlvType) {
case 0x0003: System.arraycopy(infoBuf, 0, extIP, 0, 4); break;
case 0x0004: System.arraycopy(infoBuf, 0, ip, 0, 4); break;
case 0x0005: port = Util.getWordBE(infoBuf, 0); break;
case 0x000a: ackType = Util.getWordBE(infoBuf, 0); break;
}
// #sijapp cond.end#
if (0x2711 == tlvType) {
msg2Buf = infoBuf;
}
} while (msgMarker.isNotEnd());
// Get message data and initialize marker
if (null == msg2Buf) {
return;
}
ArrayReader msg2Reader = new ArrayReader(msg2Buf, 0);
// Skip values up to (and including) SUB_MSG_TYPE2.UNKNOWN
// (before MSGTYPE)
msg2Reader.skip(msg2Reader.getWordLE());
msg2Reader.skip(msg2Reader.getWordLE());
// Get and validate message type
int msgType = msg2Reader.getWordLE();
if (!((MESSAGE_TYPE_PLAIN == msgType)
|| (MESSAGE_TYPE_URL == msgType)
|| (MESSAGE_TYPE_PLUGIN == msgType)
|| ((msgType >= 1000) && (msgType <= 1004)))) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
if (MESSAGE_TYPE_ADDED == msgType) {
DebugLog.println("msg type 0x" + Integer.toHexString(msgType));
}
// #sijapp cond.end #
return;
}
status = msg2Reader.getWordLE();
msg2Reader.skip(2 /* PRIORITY */);
// Get length of text
int textLen = msg2Reader.getWordLE();
// Get raw text
byte[] rawText = msg2Reader.getArray(textLen);
// Plain message or URL message
if (0x0001 == msgType) {
if (rawText.length == 0) {
return;
}
msg2Reader.skip(4 /* FOREGROUND */ + 4 /* BACKGROUND */);
// Check encoding (by checking GUID)
boolean isUtf8 = false;
if (msg2Buf.length >= msg2Reader.getOffset() + 4) {
int guidLen = (int) msg2Reader.getDWordLE();
if (guidLen == 38) {
byte[] guidBin = msg2Reader.getArray(guidLen);
String guid = StringUtils.byteArrayToAsciiString(
guidBin, 0, guidBin.length);
if ("{0946134E-4C7F-11D1-8222-444553540000}".equals(guid)) {
isUtf8 = true;
}
}
}
// Decode text and create Message object
String text = null;
// Decode text
if (isUtf8) {
text = StringUtils.utf8beByteArrayToString(rawText, 0, rawText.length);
} else {
text = StringUtils.byteArrayToWinString(rawText, 0, rawText.length);
}
// Forward message object to contact list
addMessage(uin, text);
sendRecivedFlag(marker.getBuffer(), uin, true);
} else if (0x0004 == msgType) { // URL
// Extended message
} else if (msgType == 0x001A) {
// #sijapp cond.if modules_XSTATUSES is "true" #
XtrazMessagePlugin plg = unpackPlugin(msg2Reader, uin);
if (null != plg) {
marker.setOffset(0);
plg.setCookie(marker);
sendPacket(plg.getPacket());
return;
}
// #sijapp cond.end #
}
//////////////////////
// Message format 4 //
//////////////////////
} else if (format == 0x0004) {
// Skip SUB_MSG_TYPE4.UIN
msgMarker.skip(4);
// Get SUB_MSG_TYPE4.MSGTYPE
int msgType = msgMarker.getWordLE();
// Only plain messages and URL messagesa are supported
if (msgType != 0x0001) return;
// Get length of text
int textLen = msgMarker.getWordLE();
// Get text
String text = StringUtils.byteArrayToWinString(
msgMarker.getArray(textLen), 0, textLen);
// Forward message to contact list
addMessage(uin, text);
}
}
private String getUinByByteLen(ArrayReader reader) {
int len = reader.getByte();
byte[] buf = reader.getArray(len);
return StringUtils.byteArrayToAsciiString(buf, 0, buf.length);
}
private String lastOfflineUin = null;
private void userOffline(SnacPacket snacPacket) {
// Get UIN of the contact that goes offline
String uin = getUinByByteLen(snacPacket.getReader());
IcqContact item = (IcqContact)getIcq().getItemByUID(uin);
if (null != item) {
boolean hasBeenOffline = !item.isOnline();
// #sijapp cond.if modules_MAGIC_EYE is "true" #
if (getIcq().isConnected() && hasBeenOffline) {
if (item.getUserId().equals(lastOfflineUin)) {
MagicEye.addAction(getIcq(), lastOfflineUin, "hiding_from_you");
}
lastOfflineUin = item.getUserId();
}
// #sijapp cond.end#
if (hasBeenOffline) {
return;
}
item.setOfflineStatus();
// Update contact list
getIcq().ui_changeContactStatus(item);
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else {
DebugLog.println("USER_OFFLINE for " + uin);
// #sijapp cond.end#
}
}
private void userOnline(SnacPacket snacPacket) {
// DC variables
byte[] internalIP = new byte[4];
byte[] externalIP = new byte[4];
int dcPort = 0;
int dcType = -1;
int authCookie = 0;
int protocolVersion = 0;
int dwFT1 = 0;
int dwFT2 = 0;
int dwFT3 = 0;
byte[] capabilities_old = new byte[0]; // Buffer for old style capabilities (TLV 0x000D)
byte[] capabilities_new = null; // Buffer for new style capabilities (TLV 0x0019)
String statusText = null;
String mood = null;
// Time variables
int idle = -1;
int online = -1;
long signon = -1;
// Get UIN of the contact changing status
int status = IcqStatusInfo.STATUS_ONLINE;
// Get data
ArrayReader marker = snacPacket.getReader();
int uinLen = marker.getByte();
String uin = StringUtils.byteArrayToAsciiString(
marker.getArray(uinLen), 0, uinLen);
marker.skip(2 /* warning level */);
// Get new status and client capabilities
int tlvNum = marker.getWordBE();
for (int i = 0; i < tlvNum; ++i) {
int tlvType = marker.getTlvType();
byte[] tlvData = marker.getTlv();
if (tlvType == 0x0006) {// STATUS
status = (int)Util.getDWordBE(tlvData, 0);
} else if (tlvType == 0x000D) {// Old style CAPABILITIES
capabilities_old = tlvData;
} else if (tlvType == 0x0019) {// New style CAPABILITIES
capabilities_new = tlvData;
} else if (tlvType == 0x000A) {// External IP
externalIP = tlvData;
} else if (tlvType == 0x000C) {// DC Infos
ArrayReader dcMarker = new ArrayReader(tlvData, 0);
internalIP = dcMarker.getArray(4);
dcPort = (int)dcMarker.getDWordBE();
dcType = dcMarker.getByte();
protocolVersion = dcMarker.getWordBE();
authCookie = (int)dcMarker.getDWordBE();
dcMarker.skip(8);
dwFT1 = (int) dcMarker.getDWordBE();
dwFT2 = (int) dcMarker.getDWordBE();
dwFT3 = (int) dcMarker.getDWordBE();
} else if (tlvType == 0x0003) {// Signon time
signon = byteArrayToLong(tlvData); // GMT
} else if (tlvType == 0x0004) {// Idle time
idle = (int)byteArrayToLong(tlvData);
} else if (tlvType == 0x000F) {// Online time
online = (int)byteArrayToLong(tlvData);
// Icon service... and new style status message
} else if (tlvType == 0x001D) {
ArrayReader iconMarker = new ArrayReader(tlvData, 0);
final int BART_STATUS_STR = 0x0002;
final int BART_STATUS_ID = 0x000E;
while (iconMarker.isNotEnd()) {
int bartType = iconMarker.getWordBE();
iconMarker.skip(1);
int recordLen = iconMarker.getByte();
if (0 == recordLen) continue;
if (BART_STATUS_ID == bartType) {
mood = StringUtils.utf8beByteArrayToString(
iconMarker.getBuffer(), iconMarker.getOffset(), recordLen);
} else if (BART_STATUS_STR == bartType) {
int len = iconMarker.getWordBE();
recordLen -= 2;
statusText = StringUtils.utf8beByteArrayToString(
iconMarker.getBuffer(), iconMarker.getOffset(), len);
}
iconMarker.skip(recordLen);
}
}
}
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.println("mood for " + uin + " " + mood + " " + statusText);
// #sijapp cond.end #
IcqContact contact = (IcqContact)getIcq().getItemByUID(uin);
if (contact != null) {
// TODO: happy flag
int flags = (status >> 16) & 0xFFFF;
// #sijapp cond.if modules_XSTATUSES is "true" #
contact.setXStatus(Icq.xstatus.createXStatus(capabilities_old, mood), null);
// #sijapp cond.end #
statusText = StringUtils.trim(statusText);
if (!StringUtils.isEmpty(statusText)) {
// #sijapp cond.if modules_XSTATUSES is "true" #
if (XStatusInfo.XSTATUS_NONE != contact.getXStatusIndex()) {
contact.setXStatusMessage(statusText);
statusText = null;
}
// #sijapp cond.end #
}
getIcq().setContactStatus(contact,
IcqStatusInfo.getStatusIndex(status & 0xFFFF, capabilities_old),
statusText);
contact.happyFlag = ((flags & FLAG_HAPPY) != 0)
|| GUID.CAP_QIP_HAPPY.equals(capabilities_old);
byte[] capa = mergeCapabilities(capabilities_old, capabilities_new);
capabilities_old = null;
capabilities_new = null;
// #sijapp cond.if modules_CLIENTS is "true" #
// Update time values
contact.setTimeOfChaingingStatus(signon);
ClientDetector.instance.execVM(contact, capa, new int[] {dwFT1, dwFT2, dwFT3}, protocolVersion);
// #sijapp cond.end #
// Update contact list
getIcq().ui_changeContactStatus(contact);
// #sijapp cond.if modules_DEBUGLOG is "true" #
} else {
DebugLog.println("USER_ONLINE for " + uin + " (0x" + Integer.toHexString(status) + ")");
// #sijapp cond.end#
}
}
// #sijapp cond.if modules_XSTATUSES is "true" #
private XtrazMessagePlugin unpackPlugin(ArrayReader reader, String uin) {
// size + XTRAZ_GUID + func
int someLen = reader.getWordLE();
int bufPos = reader.getOffset() + someLen;
int guidPos = reader.getOffset();
if (XtrazMessagePlugin.XTRAZ_GUID.equals(reader.getBuffer(), guidPos, 16)) {
reader.setOffset(bufPos);
return parseXtrazMessage(uin, reader);
}
return null;
}
private String getText(String text) {
return Util.xmlUnescape(text);
}
private String getTagContent(String xml, String tag) {
int begin = xml.indexOf("<" + tag + ">");
int end = xml.indexOf("</" + tag + ">");
if (begin >= 0 && end > begin) {
int offset = tag.length() + 2;
return getText(xml.substring(begin + offset, end));
}
return "";
}
private String makeXPromt(String promt) {
return Util.xmlEscape(promt);
}
private static String[] tags = Util.explode("title|desc", '|');
private XtrazMessagePlugin parseXtrazMessage(String uin, ArrayReader reader) {
Icq icq = getIcq();
IcqContact contact = (IcqContact)icq.getItemByUID(uin);
reader.skip(4);
int xmlLen = (int)reader.getDWordLE();
xmlLen = Math.min(xmlLen, reader.getBuffer().length - reader.getOffset());
String xml = StringUtils.utf8beByteArrayToString(
reader.getArray(xmlLen), 0, xmlLen);
// #sijapp cond.if modules_MAGIC_EYE is "true" #
if (xml.startsWith("<N>")) {
MagicEye.addAction(icq, uin, "read xtraz");
}
// #sijapp cond.end #
if (contact == null) return null;
if (xml.startsWith("<NR>")) {
String res = getTagContent(xml, "RES");
String title = StringUtils.notNull(getTagContent(res, tags[0])).trim();
String desc = StringUtils.notNull(getTagContent(res, tags[1])).trim();
String text = (title + ' ' + desc).trim();
contact.setXStatusMessage(text);
getIcq().updateStatusView(contact);
} else if (xml.startsWith("<N>")) {
if (!icq.isMeVisible(contact)) {
return null;
}
int id = icq.getProfile().xstatusIndex;
if (id < 0) {
return null;
}
String title = icq.getProfile().xstatusTitle;
String desc = icq.getProfile().xstatusDescription;
String str ="<ret event='OnRemoteNotification'><srv>"
+ "<id>cAwaySrv</id><val srv_id='cAwaySrv'>"
+ "<Root>"
+ "<CASXtraSetAwayMessage></CASXtraSetAwayMessage>"
+ "<uin>" + icq.getUserId() + "</uin>"
+ "<index>" + id + "</index>"
+ "<title>" + makeXPromt(title) + "</title>"
+ "<desc>" + makeXPromt(desc) + "</desc>"
+ "</Root></val></srv></ret>";
str = Util.replace(makeXPromt(str), "'", "'");
return new XtrazMessagePlugin(contact, "<NR><RES>" + str + "</RES></NR>");
}
return null;
}
// #sijapp cond.end #
// Converts the specific 4 byte max buffer to an unsigned long
private long byteArrayToLong(byte[] b) {
long l = 0;
l |= b[0] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
if (b.length > 3) {
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[3] & 0xFF;
}
return l;
}
// Static variables for message type;
// public static final int MESSAGE_TYPE_AUTO = 0x0000;
// public static final int MESSAGE_TYPE_NORM = 0x0001;
public static final int MESSAGE_TYPE_EXTENDED = 0x001a;
// public static final int MESSAGE_TYPE_AWAY = 0x03e8;
// public static final int MESSAGE_TYPE_OCC = 0x03e9;
// public static final int MESSAGE_TYPE_NA = 0x03ea;
// public static final int MESSAGE_TYPE_DND = 0x03eb;
// public static final int MESSAGE_TYPE_FFC = 0x03ec;
// public static final int MESSAGE_TYPE_UNKNOWN = 0x0000; // Unknown message, only used internally by this plugin
public static final int MESSAGE_TYPE_PLAIN = 0x0001; // Plain text (simple) message
// public static final int MESSAGE_TYPE_CHAT = 0x0002; // Chat request message
// public static final int MESSAGE_TYPE_FILEREQ = 0x0003; // File request / file ok message
public static final int MESSAGE_TYPE_URL = 0x0004; // URL message (0xFE formatted)
// public static final int MESSAGE_TYPE_AUTHREQ = 0x0006; // Authorization request message (0xFE formatted)
// public static final int MESSAGE_TYPE_AUTHDENY = 0x0007; // Authorization denied message (0xFE formatted)
// public static final int MESSAGE_TYPE_AUTHOK = 0x0008; // Authorization given message (empty)
// public static final int MESSAGE_TYPE_SERVER = 0x0009; // Message from OSCAR server (0xFE formatted)
public static final int MESSAGE_TYPE_ADDED = 0x000C; // "You-were-added" message (0xFE formatted)
// public static final int MESSAGE_TYPE_WWP = 0x000D; // Web pager message (0xFE formatted)
// public static final int MESSAGE_TYPE_EEXPRESS = 0x000E; // Email express message (0xFE formatted)
// public static final int MESSAGE_TYPE_CONTACTS = 0x0013; // Contact list message
public static final int MESSAGE_TYPE_PLUGIN = 0x001A; // Plugin message described by text string
// public static final int MESSAGE_TYPE_AWAY = 0x03E8; // Auto away message
// public static final int MESSAGE_TYPE_OCC = 0x03E9; // Auto occupied message
// public static final int MESSAGE_TYPE_NA = 0x03EA; // Auto not available message
// public static final int MESSAGE_TYPE_DND = 0x03EB; // Auto do not disturb message
// public static final int MESSAGE_TYPE_FFC = 0x03EC; // Auto free for chat message
}
// #sijapp cond.end #