ICQ2000 сделай сам 7

|
Автор: 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 '
|
В исходном коде это выглядит так:
// 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 | ||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
Передадим пакет и от сервера получим 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,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 "
|
И "нарешти" - код для приема 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);
...следите за обновлениями сайта