Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
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 используется во множестве различных запросов. Я лишь выделю те строки, которые будут включены в наш запрос, а именно:

  1. заголовок самого SNAC(15,2);
  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);

...следите за обновлениями сайта

Проект Delphi World © Выпуск 2002 - 2024
Автор проекта: USU Software
Вы можете выкупить этот проект.