Автор: Alexander
Vaga WEB-сайт: http://icq2000cc.hobi.ru
Для разбора конкретного примера возьмем ситуацию, когда мы запрашиваем у
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) ).
В описании это находится вот здесь:
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 '' name of required data '' 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 .............. ..............
..............
|
В исходном коде это выглядит так:
// 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), которая
соответствует таблице:
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 ""field-type"" 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
|
И "нарешти" - код для приема SNAC(15,3). Множественные комментарии, кажется
тут уже излишни.
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);
...следите за обновлениями сайта |