procedure TICQClient.HSnac0407(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
var
ITime, IRandomID: LongWord;
ULen: Word;
c, i: Word;
TlvType:Word;
ft_pkt, ack_pkt: TRawPkt;
chunks:
array[0..49]
of Byte;
CharsetNumber, CharsetSubset: Word;
Msg, UIN:
String;
Status: LongWord;
MsgType: Word;
MsgFlag: Byte;
Answer: LongWord;
aPort: Word;
aIP: DWord;
extIP: DWORD;
wAck: Word;
Request: Boolean;
// dwOnlineSince: DWORD;
Desc,
URL:
String;
v: Byte;
atype:
String;
XML:
String;
XMLTime, XMLSource, XMLSender, XMLText:
String;
FName, FDesc:
String;
FSize: LongWord;
FFSeq, FFSeq2: Word;
Rec: TFTRequestRec;
TCMD:
String;
List: TStringList;
PSender, PEmail, PSenderIP:
string;
chan:integer;
begin
Request := False;
ITime := GetInt(Pkt, 4);
//Time(system up since): time(NULL) - ITime div 1000
//'System up since: ' + DateTimeToStr(DateTimeToUnix(Now) - ITime div 1000);
//Seen by ICQ2003x
IRandomID := GetInt(Pkt, 2);
//RandomID
Inc(Pkt^.Len, 2);
//Unknown: empty
Msg := '
';
{Subtypes}
chan:=GetInt(Pkt, 2);
UIN := GetStr(Pkt, GetInt(Pkt, 1));
Inc(Pkt^.Len, 2);
c := GetInt(Pkt, 2);
//A count of the number of following TLVs.
for i := 0
to c - 1
do //Skip all TLVs
begin
Inc(Pkt^.Len, 2);
if (Pkt^.Len>=Flap.DataLen)
then exit;
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
case chan
of
1:
//Simply(old-type) message
begin
if GetInt(Pkt, 2) = 2
then //TLV with message remain
begin
Inc(Pkt^.Len, 4);
//TLV length + Unknown const
Inc(Pkt^.Len, GetInt(Pkt, 2));
//Counts of following bytes + following bytes
Inc(Pkt^.Len, 2);
//x0101, Unknown, constant
ULen := GetInt(Pkt, 2) - 4;
//Length of the message + 4
// Support for other charsets by Yegor
CharsetNumber:=GetInt (Pkt, 2);
//The encoding used for the message.
//0x0000: US-ASCII
//0x0002: UCS-2BE (or UTF-16?)
//0x0003: local 8bit encoding, eg iso-8859-1, cp-1257, cp-1251.
//Beware that UCS-2BE will contain zero-bytes for characters in the US-ASCII range.
CharsetSubset:=GetInt (Pkt, 2);
//Unknown; seen: 0x0000 = 0, 0xffff = -1.
// Inc(Pkt^.Len, 4); //Unknown seems to be constant
Msg := GetStr(Pkt, ULen);
//The actual message text. There will be no ending NULL.
if not ((CharsetNumber=0)
or (CharsetNumber=3))
then Msg:=UTF8ToStrSmart (Msg);
if FDoPlain
then Msg:=RTF2Plain (Msg);
if (Length(Msg) > 0)
and Assigned(OnMessageRecv)
then
FOnMsg(Self, Msg, UIN);
end;
end;
2:
//Advanced(new-type)
begin
while(Pkt^.Len<Flap.DataLen)
do
begin
if GetInt(Pkt, 2) = 5
then
begin
Inc(Pkt^.Len, 2);
if GetInt(Pkt, 2) <> 0
then //ACKTYPE: 0x0000 - This is a normal message
Exit;
Inc(Pkt^.Len, 16);
//File signature
Inc(Pkt^.Len, 8);
//TIME + RANDOM
for i := 0
to 5
do
begin
TlvType := GetInt(Pkt, 2);
if TlvType = $2711
then //Searching for TLV(2711) (with sources)
begin
// * eraser 21.04.2004
// todo: AdvancedOnlineInfo event: aIP, aPort, ITime, Online Since
Inc(Pkt^.Len, 2);
//TLV Length
Move(Ptr(LongWord(Pkt) + Pkt^.Len)^, chunks, 47);
if GetInt(Pkt, 1) <> $1B
then //If this value is not present, this is not a message packet. Also, ICQ2001b does not send an ACK, SNAC(4,B), if this is not 0x1B.
Exit;
Inc(Pkt^.Len, 26);
FFSeq := GetInt(Pkt, 2);
Inc(Pkt^.Len, 16);
// * eraser 21.04.2004
MsgType := GetInt(Pkt, 1);
//message type (e.g. ea = AWAY)
MsgFlag := GetInt(Pkt, 1);
//message flags (e.g. 03 = MFLAG_AUTO)
Answer := GetInt(Pkt, 4);
//Read away-msg: seen 00 00 00 00
//Send away-msg: seen zero and non zero
//Away-Msgs respond from ICQ Lite
if ((MsgType
and $E0) = $E0)
and (MsgFlag = MFLAG_AUTO)
then
begin
if Request = False
then begin
Msg := GetLNTS(Pkt);
//message string (null-terminated)
if Assigned(OnAutoMsgResponse)
then
FOnAutoMsgResponse(Self, UIN, IRandomID, MsgType, Msg);
// Exit;
end;
(*
else (todo) who is reading my away-msg event
*)
end else
//
if MsgType = M_FILE
then //File request
begin
FDesc := GetLNTS(Pkt);
//File description
Inc(Pkt^.Len, 4);
//Unknown: 00 00 00 00
FName := GetLNTS(Pkt);
//File name
FSize := GetLInt(Pkt, 4);
//File size
{Set the records' items}
Rec.ITime := ITime;
Rec.IRandomID := IRandomID;
Rec.UIN := StrToInt64(UIN);
Rec.FileSize := FSize;
Rec.Description := FDesc;
Rec.FileName := FName;
Rec.Seq := FFSeq;
Rec.ReqType := $01;
if Assigned(OnFTRequest)
then
FOnFTRequest(Self, Rec);
Exit;
end else
if MsgType = M_ADVANCED
then //Advanced message container
begin
GetLNTS(Pkt);
//Empty message (contains only a null terminator)
Inc(Pkt^.Len, 2);
//Following length
Inc(Pkt^.Len, 16);
//Signature
Inc(Pkt^.Len, 2);
//Unknown: empty
TCMD := GetDWStr(Pkt);
//Text command
If (Pos('
File', TCmd) > 0)
or (Pos(#$D4#$E0#$E9#$EB, TCmd) > 0)
Then {Fix for russian users from Yegor} //Someone is sending us a file
begin
Inc(Pkt^.Len, 19);
fDesc := GetDWStr(Pkt);
aPort := GetInt(Pkt, 2);
FFSeq2:= GetInt(Pkt, 2);
fName := GetWStr(Pkt);
fSize := GetInt(Pkt, 4);
Rec.Port := aPort;
Rec.ITime := ITime;
Rec.IRandomID := IRandomID;
Rec.UIN := StrToInt64(UIN);
Rec.FileSize := FSize;
Rec.Description := FDesc;
Rec.FileName := FName;
Rec.Seq := FFSeq;
// Send Ack through Server.
Rec.ReqType := $01;
//
//NOTE From NighTrader: This is not working correctly,
//I was doing this wrong, if you have any knowladge of
//how this works please ICQ me @ 30391169.
if Assigned(OnFTRequest)
then
FOnFTRequest(Self, Rec);
Exit;
end else
if (Pos('
Request For Contacts', TCMD) > 0)
or (Pos(#$C7#$E0#$EF#$F0#$EE#$F1#$20#$CA#$EE#$ED#$F2#$E0#$EA#$F2#$EE#$E2, TCmd) > 0)
then
begin
Inc(Pkt^.Len, 15);
//15 unknown bytes
Inc(Pkt^.Len, 4);
//Following length
Msg := GetDWStr(Pkt);
//Message containing a reason
if Assigned(OnContactListRequest)
then
FOnContactListReq(Self, UIN, Msg);
end else
if (Pos('
Contacts',TCMD) > 0 )
or (Pos(#$CA#$EE#$ED#$F2#$E0#$EA#$F2#$
FB, TCMD) > 0)
then
begin
Inc(Pkt^.Len, 4);
//Following length
Msg := GetDWStr(Pkt);
//Message containing a list with contacts
List := TStringList.Create;
//Create temporary list
ParseContacts(Msg, List);
//Parse message with contacts
if Assigned(OnContactListRecv)
then
FOnContactListRecv(Self, UIN, List);
end else
if (Pos('
Web Page Address (URL)',TCMD) > 0)
or (Pos(#$CF#$EE#$F1#$EB#$E0#$F2#$FC#$20#$D1#$F1#$
FB#$EB#$EA#$F3#$20#$ED#$E0#$20#$E2#$E5#$E1#$2D#$F1#$F2#$F0#$E0#$ED#$E8#$F6#$F3,TCmd) > 0)
then
begin
// Handle URL Msg
Inc(Pkt^.Len, 19);
Msg := GetDWStr(Pkt);
if Pos(#$FE, Msg) <> -1
then begin // Break apart strings
Desc := Copy(Msg, 1, Pos(#$FE, Msg) - 1);
URL := Copy(Msg, Length(Desc) + 2, 255);
if Assigned(OnURLRecv)
then
FOnURL(Self, Desc,
URL, UIN);
end;
end;
end
else
Msg := GetLNTS(Pkt);
//The actual message text. There will be ending NULL.
{Sending ACK of the message}
PktInit(@ack_pkt, 2, FSeq);
//Channel 2
PktSnac(@ack_pkt, $04, $0B, 0, 0);
//SNAC(x04/x0B)
Move(Ptr(LongWord(Pkt) + TSNACSZ)^, Ptr(LongWord(@ack_pkt) + ack_pkt.Len)^, 10);
//First 10 bytes of TLV(2711)
Inc(ack_pkt.Len, 10);
//Skip first 10 bytes copied from TLV(2711) which were added before
PktLStr(@ack_pkt, UIN);
//User's UIN
PktInt(@ack_pkt, $0003, 2);
//00 03
PktAddArrBuf(@ack_pkt, @chunks, 47);
//First 47 bytes of source packet (with message)
PktInt(@ack_pkt, $00000000, 4);
//00 00 00 00
//If it's an auto-away message request
if (MsgType
and $E0 = $E0)
and Request
then
PktLNTS(@ack_pkt, FAutoAwayMsg)
//Auto-away message
else begin
PktInt(@ack_pkt, 1, 1);
//01
PktInt(@ack_pkt, 0, 4);
//00 00 00 00
PktInt(@ack_pkt, 0, 2);
//00 00
PktInt(@ack_pkt, $FFFFFF00, 4);
//FF FF FF 00
end;
PktFinal(@ack_pkt);
FSock.SendData(ack_pkt, ack_pkt.Len);
if (Length(Msg) > 0)
then
begin
if MsgType = M_PLAIN
then
begin
if FDoPlain
then Msg := Rtf2Plain(Msg);
//Convert message from RTF to plaintext when needed
if Assigned(OnMessageRecv)
then
FOnMsg(Self, Msg, UIN);
end else
if MsgType = M_URL
then
begin
Desc := Copy(Msg, 0, Pos(#$fe, Msg) - 1);
URL := Copy(Msg, Pos(#$fe, Msg) + 1, Length(Msg) - Pos(#$fe, Msg));
if Assigned(OnURLRecv)
then
FOnURL(Self, Desc,
URL, UIN);
end;
end;
Exit;
end else
if TlvType = 5
then // TVL(5) Get a Port
begin
Inc(pkt^.Len, 2);
// Skip Length
aPort := GetInt(Pkt, 2);
// Get Port Number
end else
// *eraser 21.04.2004
{
if TlvType = 6 then // TLV(6) Status
begin
Inc(pkt^.Len, 4);
Status := GetInt(Pkt, 4);
end else
}
if TlvType = $0004
then // TLV(4) Get an ExtIP
begin
Inc(pkt^.Len, 4);
// skip length
extIP := GetInt(Pkt, 4);
// Get IP Number
end else
if TlvType = $000F
then // TLV(F)
begin
Inc(pkt^.Len, 2);
// skip length
Request := True;
// possibly it is a request
end else
if TlvType = $000A
then // TLV(A)
begin
Inc(pkt^.Len, 2);
// 0x0002 skip length
wAck := GetInt(Pkt, 2);
// seen 0001 - req (unk 0x000F 0x0000)
// seen 0002 - reply (IP, Port)
end else
//
if TlvType = 3
then // TLV(3) Get an IntIP
begin
Inc(pkt^.Len,2);
// Skip Length
aIP := GetInt(Pkt,4);
// Get IP Number
end else
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
end else
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
// todo special user online event
end;
4:
//Another message type
begin
UIN := GetLStr(Pkt);
for i := 0
to 4
do
begin
v := GetInt(Pkt, 1);
if (v = 5)
or ((GetInt(Pkt, 1) = 5)
and (v = 0))
then //TLV(5) was found
begin
if v = 5
then //Some modifications for MAC clients
Inc(Pkt^.Len, 40)
else
Inc(Pkt^.Len, 2);
GetLInt(Pkt, 4);
//UIN
MsgType := GetLInt(Pkt, 2);
//Message-type
Msg := GetLNTS(Pkt);
//Message
if MsgType = $1a
then //Probably advanced msg format
begin
Inc(Pkt^.Len, 20);
//20 unknown bytes
atype := GetDWStr(Pkt);
//Advanced msg sub-type
if Pos ('
ICQSMS', aType)<>0
then //Corresponds to received SMS message in XML formatted message
begin
Inc(Pkt^.Len, 3);
//00 00 00
Inc(Pkt^.Len, 4);
//4-byte little endian length of the following data
XML := GetStr(Pkt, GetLInt(Pkt, 4));
//XML entry of SMS response
XMLSource := GetXMLEntry('
source',
XML);
//Source, usually: 'ICQ'
XMLSender := GetXMLEntry('
sender',
XML);
//Source cellular number
XMLText := GetXMLEntry('
text',
XML);
//Text of reply
XMLTime := GetXMLEntry('
time',
XML);
//Time of sending reply
if Assigned(OnSMSReply)
then
FOnSMSReply(Self, XMLSource, XMLSender, XMLTime, UTF8ToStrSmart(XMLText));
end;
Exit;
end;
if (Length(Msg) > 0)
then
begin
if MsgType = M_PLAIN
then
begin
if FDoPlain
then Msg := Rtf2Plain(Msg);
//Convert message from RTF to plaintext when needed
if Assigned(OnMessageRecv)
then
FOnMsg(Self, Msg, UIN);
end
else if MsgType = M_URL
then
begin
Desc := Copy(Msg, 0, Pos(#$fe, Msg) - 1);
URL := Copy(Msg, Pos(#$fe, Msg) + 1, Length(Msg) - Pos(#$fe, Msg));
if Assigned(OnURLRecv)
then
FOnURL(Self, Desc,
URL, UIN);
end else
if MsgType = M_WEB_PAGE
then // Updated by Saif.N * To Support Web Page Message
begin
PSender := Copy(Msg, 1, Pos(#$fe, Msg) - 1);
Msg := Copy(Msg,Length(PSender)+4, Length(Msg));
PEmail := Copy(Msg, 1 , Pos(#$fe, Msg)-1);
Msg := Copy(Msg,Length(PEmail)+1, Length(Msg));
PSenderIP:= Copy(Msg, Pos('
IP:', Msg)+4, Pos(#$D, Msg)-Pos('
IP:', Msg)-4);
Msg := Copy(Msg, Pos(#$A,Msg) + 1, Length(Msg));
if Assigned(OnWPagerRecv)
then
FonWPager(Self, PSender, PEmail, PSenderIP, Msg);
end;
end;
Exit;
end else
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
end;
end;
end;