📄 icqclient.pas
字号:
end;
//Sending CLI_GOODBYE
PktInit(@pkt, 4, FSeq);
PktFinal(@pkt);
FSock.SendData(pkt, pkt.Len);
FSock.Disconnect;
//Assigning new IP and Port to connect to in second attemp
InitNetICQ;
FSock.Host := Copy(FData, 0, Pos(':', FData) - 1);
FSock.Port := StrToInt(Copy(FData, Pos(':', FData) + 1, Length(FData) - Pos(':', FData)));
if (FSock.Port = 0) then
begin
OnIntError(nil, ERR_PROTOCOL, 'Received malformed login packet');
FTOnConnectError(Self);
Exit;
end;
FSock.Connect;
end;
end;
end;
{////////////////////////////////////////////////////////////////////////////////////////////////////}
procedure TICQClient.SetStatus(NewStatus: LongWord);
var
pkt: TRawPkt;
begin
if not LoggedIn then Exit;
if (StatusToStr(FStatus) = 'Invisible') and (StatusToStr(NewStatus) <> 'Invisible') then
begin
CreateCLI_ADDINVISIBLE(@pkt, FInvisibleLst, FSeq);
FSock.SendData(pkt, pkt.Len);
end else
if (StatusToStr(NewStatus) = 'Invisible') and (StatusToStr(FStatus) <> 'Invisible') then
begin
CreateCLI_ADDVISIBLE(@pkt, FVisibleLst, FSeq);
FSock.SendData(pkt, pkt.Len);
end;
CreateCLI_SETSTATUS_SHORT(@pkt, NewStatus, FSeq);
FSock.SendData(pkt, pkt.Len);
FStatus := NewStatus;
end;
{Handling packet with messages}
procedure TICQClient.HSnac0407(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
var
ITime, IRandomID: LongWord;
ULen: Word;
c, i: Word;
ack_pkt: TRawPkt;
chunks: array[0..49] of Byte;
Msg, UIN: String;
MsgType: Word;
Desc, URL: String;
v: Byte;
atype: String;
XML: String;
XMLTime, XMLSource, XMLSender, XMLText: String;
FName, FDesc: String;
FSize: LongWord;
FFSeq: Word;
Rec: TFTRequestRec;
TCMD: String;
List: TStringList;
begin
ITime := GetInt(Pkt, 4); //Time
IRandomID := GetInt(Pkt, 2); //RandomID
Inc(Pkt^.Len, 2); //Unknown: empty
Msg := '';
{Subtypes}
case GetInt(Pkt, 2) of
1: //Simply(old-type) message
begin
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);
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
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
Inc(Pkt^.Len, 4); //Unknown seems to be constant
Msg := GetStr(Pkt, ULen); //The actual message text. There will be no ending NULL.
if (Length(Msg) > 0) and Assigned(OnMessageRecv) then
FOnMsg(Self, Msg, UIN);
end;
end;
2: //Adnavced(new-type)
begin
UIN := GetStr(Pkt, GetInt(Pkt, 1));
for c := 0 to 5 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
if GetInt(Pkt, 2) = $2711 then //Searching for TLV(2711) (with sources)
begin
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);
MsgType := GetInt(Pkt, 1);
Inc(Pkt^.Len, 5);
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 := StrToInt(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 TCMD = 'Contacts' 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 TCMD = 'Request For Contacts' 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;
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 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 := Rtf2Txt(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
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
end else
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
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 atype = 'ICQSMS' 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 := Rtf2Txt(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
Inc(Pkt^.Len, GetInt(Pkt, 2));
end;
end;
end;
end;
{Handling old type packets ICQ_FROMSRV}
procedure TICQClient.HSnac1503(Flap: TFlapHdr; Snac: TSnacHdr; Pkt: PRawPkt);
var
FMsgType: Word;
lpkt: TRawPkt;
FNick, FFirst, FLast, FEmail, FCity,
FState, FPhone, FFax, FStreet, FCellular,
FZip, FCountry, FCompany, FDepartment,
FPosition, FOccupation, FHomePage,
FLang1, FLang2, FLang3, FAbout: String;
FTimeZone: Byte;
FPublishEmail: Boolean;
FAge, FYear: Word;
FGender, FMonth, FDay: Byte;
Msg, UIN, URL, Desc: String;
List, List2: TStringList;
C, i: Byte;
WW: Word;
FStatus: Word;
cmd: Word;
seq: Word;
FSmsSource, FSmsDeliverable, FSmsNetwork, FMsgId: String;
FAuthorize: Byte;
begin
if GetInt(Pkt, 2) = 1 then //TLV(1)
begin
Inc(Pkt^.Len, 8);
case GetInt(Pkt, 2) of
$4100: //SRV_OFFLINEMSG
begin
Inc(Pkt^.Len, 2); //The sequence number this packet is a response to.
UIN := IntToStr(GetLInt(Pkt, 4)); //Source UIN
Inc(Pkt^.Len, 6); //Date/time etc...
FMsgType := GetLInt(Pkt, 2); //The type of message sent, like URL message or the like.
Msg := GetLNTS(Pkt);
if FDoPlain then Msg := Rtf2Txt(Msg); //Convert message from RTF to plaintext when needed
if FMsgType = M_PLAIN then
begin
if Assigned(OnOfflineMsgRecv) then
FOnOffMsg(Self, Msg, UIN);
end else
if FMsgType = 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(OnOfflineURLRecv) then
FOnOffURL(Self, Desc, URL, UIN);
end;
end;
$4200: //All offline messages were sent, so we ACKING them
begin
FSeq2 := 2;
CreateCLI_ACKOFFLINEMSGS(@lpkt, FLUIN, FSeq, FSeq2);
FSock.SendData(lpkt, lpkt.Len);
end;
$da07: //SRV_META
begin
seq := GetLInt(Pkt, 2);
cmd := GetInt(Pkt, 2);
case cmd of
$0100: //SRV_SMSREFUSED
begin
if Assigned(OnSMSRefused) then
FOnSMSRefused(Self);
end;
$9600: //SRV_SMSACK
begin
if GetInt(Pkt, 1) <> $0a then Exit;
Inc(Pkt^.Len, 12);
Msg := GetStr(Pkt, GetLInt(Pkt, 2));
FSmsSource := GetXML
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -