Для
разбора конкретного примера возьмем ситуацию, когда мы запрашиваем
у ICQ-сервера оффлайновые сообщения (т.е. те, которые
накопились на сервере, пока нас не было в онлайне).
Запрос
оффлайновых сообщений делаем с помощью SNAC(15,2), а
ответ на него получим соответственно в SNAC(15,3). Оба
этих SNACa имеют очень простой формат. Они содержат в себе только
по одному TLV, а именно TLV(1). На первый взгляд все
очень просто. Но... TLV(1), в свою очередь, имеет очень ветвистую
структуру. (Такие особенности имеют и некоторые другие SNACи,
например, SNAC(4,6) для передачи и SNAC(4,7) для приема сообщений).
В заметках
к протоколу ICQv7 от Massimo Melina есть описание SNAC(15,2).
Этот SNAC используется во множестве различных запросов. Я лишь
выделю те строки, которые будут включены в наш запрос, а именно:
- заголовок самого SNAC(15,2);
- TLV(1), который включает в себя:
длину, следующих далее
данных,
наш UIN,
тип запроса ($3С00),
cookie (по которому мы
узнаем ответный SNAC(15,3) ).
В описании это находится вот здесь:
----- cut -----
SNAC 15,02
TLV(1)
WORD (LE) bytes remaining, useless
UIN my uin
WORD type
WORD cookie
type=3C00 // ask for offlines messages
nothing
type=3E00 // ack to offline messages,
nothing type=D007
WORD subtype
subtype=9808 xml-stype in an LNTS
LNTS '<key>' name of required data '</key>'
subtype=1F05 // simple query info
UIN user to request info subtype=B204 // query info about user
UIN user to request info subtype=D004 // query my info
UIN my uin
..............
..............
..............
----- cut -----
В исходном коде это выглядит так:
unit Main; |
// Get offline messages
// создаем FLAP-заголовок с Channel_ID=2 и SEQ++
tmp := CreatePacket(2,SEQ);
// добавляем SNAC-заголовок SNAC(15,2)
SNACAppend(tmp,$15,$2);
// добавляем TLV(1) ($0001-Type, $000A-Length)
PacketAppend32(tmp,dswap($0001000A));
// добавляем саму Value Для TLV(1)
PacketAppend16(tmp, swap($0800));// бесполезная длина
PacketAppend32(tmp, UIN); // наш UIN
PacketAppend16(tmp, swap($3C00));// тип запроса
PacketAppend16(tmp, swap($0200));// cookie
PacketSend(tmp);
M(Memo,'> Get offline messages');
|
|
Этот кусок кода сгенерирует следующий дамп:
2A 02 36 86 00 18 00 15
00 02 00 00 00 87 00 02
00 01 00 0A 08 00 XX XX
XX XX 3C 00 02 00
Разпишем его в табличном виде для лучшего восприятия:
FLAP |
Command Start |
2A |
Channel ID |
02 |
Sequence Number |
36 86 |
Data Field Length |
00 18 |
SNAC (15,
02) |
Family ID |
00
15 |
SubType ID |
00
02 |
Flags[0] |
00 |
Flags[1] |
00 |
Request ID |
00 87 00 02 |
TLV
(1) |
Type |
00
01 |
Length |
00
0A |
Value |
08
00
|
|
XX
XX XX XX |
наш UIN |
3C
00 |
запрос
на оффлайновые сообщения |
02
00 |
cookie |
|
|
|
|
Передадим пакет и от сервера получим FLAP-пакет с таким дампом:
2A 02 74 6D 00 4D 00 15
00 03 00 01 00 87 00 02
00 01 00 3F 3D 00 XX XX
XX XX 41 00 02 00 F8 5F
F1 08 D2 07 02 0C 10 12
01 00 25 00 EF F0 E8 E2
E5 F2 0D 0A FD F2 EE 20
F2 E5 F1 F2 EE E2 EE E5
20 F1 EE EE E1 F9 E5 ED
E8 E5 20 21 21 21 0D 0A
00 00 00
И снова распишем его в таблицу:
FLAP |
Command Start |
2A |
Channel ID |
02 |
Sequence Number |
74 6D |
Data Field Length |
4D 00 |
SNAC (15,
03) |
Family ID |
00
15 |
SubType ID |
00
03 |
Flags[0] |
00 |
Flags[1] |
01 |
Request ID |
00 87 00 02
(такой же как и в запросе) |
TLV
(1) |
Type |
00
01 |
Length |
00
3F |
Value |
3D
00
|
|
XX
XX XX XX |
наш UIN |
41
00 |
тип:
оффлайновое сообщение |
02
00 |
cookie
(как и в запросе) |
тело
сообщения |
XX
XX XX XX |
его
UIN |
D2 07 |
год
(2002) |
02 |
месяц
(февраль) |
0C |
день
(12) |
10 |
час
(16) |
12 |
минуты
(18) |
01 |
под-тип
сообщения
(обычное) |
00 |
флаги
сообщения (?) |
25
00 |
длина
сообщения (37) |
EF
F0 E8 E2 E5 F2 0D 0A FD F2 EE
20 F2 E5 F1 F2 EE E2 EE E5 20
F1 EE EE E1 F9 E5 ED E8 E5 20
21 21 21 0D 0A 00 |
текст
сообщения:
"привет
это тестовое сообщение !!!" |
00
00 |
присутствют,
если сообщение единственное |
|
|
|
|
В протокольных заметках я выделю ту часть описания SNAC(15,3),
которая соответствует таблице:
----- cut -----
SNAC 15,03 TLV(1) WORD (LE) bytes remaining, useless UIN my uin WORD message-type WORD cookie message-type = 4100 // offline message UIN his uin WORD year (LE) BYTE month (1=jan) BYTE day BYTE hour (GMT time) BYTE minutes BYTE msg-subtype BYTE msg-flags LNTS msg WORD 0000, present only in single messages message-type = 4200 // end of offline messages BYTE unknown, usually 0 message-type = D007 2 BYTE unknown, usually 98 08 WORD length of the following NTS NTS "<key>"field-type"</key>" field-type = DataFilesIP 6 BYTE unk, usually 2A 02 44 25 00 31 message-type = DA07 3 BYTE subtype subtype=A4010A // wp-full-request result wp-result-info ..............
..............
..............
subtype=B4000A // ack to remove user empty subtype=AA000A // ack to change password empty ----- cut -----
И "нарешти" - код для приема SNAC(15,3). Множественные
комментарии, кажется тут уже излишни.
unit Main; |
procedure TForm1.SNAC_15_3(p:PPack);
var MessageType,Cookie : word;
myUIN,hisUIN : longint;
year,month,day,hour,minute,typemes,subtypemes,lenmes : word;
tmp : PPack;
begin
// просто пролетаем над началом TLV(1)
PacketRead32(p);
PacketRead16(p);
// а дальше имена переменных объясняют больше, чем комментарии
myUIN := PacketRead32(p);
MessageType := swap(PacketRead16(p));
Cookie := swap(PacketRead16(p));
M(Memo,'< Cookie: $'+inttohex(Cookie,4));
case MessageType of
$4100: begin // OFFLINE MESSAGE
hisUIN := PacketRead32(p);
M(Memo,'< Message-Type: $'+inttohex(MessageType,4));
M(Memo,'< OFFLINE MESSAGE from UIN: '+s(hisUIN));
year := PacketRead16(p);
month := PacketRead8(p);
day := PacketRead8(p);
hour := PacketRead8(p);
minute := PacketRead8(p);
typemes := PacketRead8(p);
subtypemes := PacketRead8(p);
lenmes := PacketRead16(p);
DoMsg(false,typemes,lenmes,PCharArray(@(p^.data[p^.cursor])),
hisUIN,UTC2LT(year,month,day,hour,minute));
end;
end;
end;
|
|
Тут можно на недельку
передохнуть...
В скором времени я добавлю
такие модули:
- передача сообщений (SendMess);
- прием сообщений (MessFrom);
- информация о пользователе (UserInfo);
- поиск пользователей по разным критериям (SearchUser);
...следите за обновлениями сайта |