Протоколы прикладного уровня: Jabber/XMPP часть1

Прочитав статью и испробовав команды, научимся
–Соединяться с Jabber сервером
–Логиниться
–Менять статусы
–Отправлять сообщения
–Отключаться

И все это на чистом XML

В принципе, можно статью назвать “Введение в XMPP” или типа того… Но суть не изменится
Приступим-же!

Простая Jabber сессия представляет из себя следующую последовательность операций:

  • Соединение с сервером
  • Создание потока
  • Включение шифрования и создание нового потока в шифрованном канале (опционально)
  • Аутентификация
  • Привязывание (bind) потока к ресурсу (имя@сервер/ресурс)
  • Создание сессии
  • Рассылка статуса “доступен”
  • Отправка/получение сообщений, статусы, ростер, “визитные карточки”, работа с сервисами и транспортами и т.п.
  • Рассылка статуса “отключен”
  • Закрытие потока
  • Отключение от сервера

Попробуем реализовать эту схему. В моем эксперименте используется сервер jabber.ru, полагается что аккаунт на нем у вас уже есть.
В листинге ниже все мои комментарии, которые располагаются внутри XML блока, находятся в тегах XML комментариев , весь остальной XML представлен без изменений (разве что добавлены переносы строк для читаемости). Если соберетесь вводить все это в консоли (это вполне реально, я сам так и делал), желательно комментарии и переводы строк удалить.
Блок XML, отправленный клиентом обозначается –C: , переданный сервером –S:

Соединение с сервером

Соединяемся с сервером jabber.ru по 5222 порту

telnet jabber.ru 5222
Trying 213.180.203.19…
Connected to pluton.relax.ru.
Escape character is ‘^]’.

Создание потока

Отправляем серверу начало XML потока , в котором указан в том числе адрес сервера (jabber.ru)
–C:

[codesyntax lang=”xml”]<stream:stream xmlns="jabber:client" to="jabber.ru" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" >[/codesyntax]

В ответ приходит начало XML потока сервера:
–S:

[codesyntax lang=”xml”]<!–?xml version=’1.0′?–>
<stream:stream xmlns=’jabber:client’ xmlns:stream=’http://etherx.jabber.org/streams’ id=’2689330648′ from=’jabber.ru’ version=’1.0′ xml:lang=’en’>
<!– И список поддерживаемых на данный момент действий –>
<stream:features>
<!– Сервер поддерживает TLS шифрование потока (встроено в ядро Jabber протокола, но не обязательно, поддерживается большинством популярных серверов) Когда TLS шифрование включено, в Gajim около имени учетной записи отображается ключик –>
<starttls xmlns=’urn:ietf:params:xml:ns:xmpp-tls’/>
<!– Сервер поддерживает сжатие потока –>
<compression xmlns=’http://jabber.org/features/compress’>
<!– Сжатие осуществляется gz библиотекой –>
<method>zlib</method>
</compression>
<!– Перечисление механизмов SASL аутентификации –>
<mechanisms xmlns=’urn:ietf:params:xml:ns:xmpp-sasl’>
<!– Поддерживается механизм DIGEST-MD5 –>
<mechanism>DIGEST-MD5</mechanism>
<!– Поддерживается механизм PLAIN –>
<mechanism>PLAIN</mechanism>
</mechanisms>
<!– Поддерживается регистрация на сервере –>
<register xmlns=’http://jabber.org/features/iq-register’/>
</stream:features>[/codesyntax]

Аутентификация

Шифрование TLS включать не будем, не доросли еще))
Аутентификация в Jabber реализуется с помощью SASL (Simple Authentication and Security Layer). Из предложенных механизмов рекомендуется использовать механизм DIGEST-MD5, но он достаточно сложен  в реализации, поэтому для начала рассмотрим механизм PLAIN.
Отсылается блок с атрибутом mechanism=”PLAIN” и содержащий закодированную base64 строку, в которую входит:
{идентификатор авторизации напр. user1@jabber.ru (не обязательно)}ASCII символ \x00{имя пользователя, напр. user1}ASCII символ \x00{пароль в открытом виде, напр 123456}
ASCII символ \x00 – это символ ASCII NUL с 16-ричным кодом 00
такую строку можно получить, например в PHP: base64_encode( $id.”\x00″.$login.”\x00″$pass );
Можете попробовать здесь: Подготовка данных для SASL аутентификации
–C:

[codesyntax lang=”xml”]<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">dXNlcjFAamFiYmVyLnJ1AHVzZXIxADEyMzQ=</auth>[/codesyntax]

В ответ от сервера приходит сообщение об успешной авторизации (success)
–S:

[codesyntax lang=”xml”]<success xmlns=’urn:ietf:params:xml:ns:xmpp-sasl’/>[/codesyntax]

ВНИМАНИЕ! НИКОГДА не используйте такой метод аутентификации (передача пароля и логина в открытом виде по незашифрованному каналу) в реальных сетевых программах. Можно так поступать, для небольшого повышения производительности, если клиент (бот) и Jabber сервер находятся в одной защищенной сети или на одном сервере. В настоящих сетевых Jabber приложениях либо используют TLS шифрование всего потока, либо метод аутентификации DIGEST-MD5, а чаще – и то и другое одновременно.

После успешной аутентификации необходимо начать новый XML поток
–C:

[codesyntax lang=”xml”]<stream:stream xmlns="jabber:client" to="jabber.ru" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" >[/codesyntax]

Новый поток от сервера:
–S:

[codesyntax lang=”xml”]<?xml version=’1.0′?>
<!– Список доступных действий изменился –>
<stream:features>
<!– Нужно привязать (bind) поток к определенному ресурсу –>
<bind xmlns=’urn:ietf:params:xml:ns:xmpp-bind’/>
<!– Нужно начать XMPP-сессию –>
<session xmlns=’urn:ietf:params:xml:ns:xmpp-session’/>
</stream:features>[/codesyntax]

Привязывание (bind) потока к ресурсу

iq – запросы к серверу. Используются для запроса/передачи какой-либо информации (ростер, “визитные карточки” и пр.)  Имеют атрибуты type и id
type – один из списка: get (запрашивает данные), set (отправляет данные | устанавливает/заменяет какое-либо значение), result (ответ на успешное выполнение get или set, может содержать запрашиваемые данные), error (ответ на ошибочный запрос get или set)
id – просто каждый последующий iq запрос увеличивает id на единицу (инкрементируется). Сервер, отвечая на iq запрос повторяет его id. Это связано с тем, что иногда отсылается сразу несколько различных iq запросов.
В ответ на get или set запрос должен возвращаться ответ типа result (или error, если что-то не так)

Закрепляем этот поток за ресурсом “telnet”
–C:

[codesyntax lang=”xml”]<iq type="set" id="9746"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<resource>telnet</resource>
</bind>
</iq>[/codesyntax]

В ответ приходит iq с типом result
–S:

[codesyntax lang=”xml”]<iq id=’9746′ type=’result’>
<bind xmlns=’urn:ietf:params:xml:ns:xmpp-bind’>
<!– полный JID имя@сервер/ресурс –>
<jid>user1@jabber.ru/telnet</jid>
</bind>
</iq>[/codesyntax]

Создание сессии

Затем сервер требовал создать сессию… Это нужно для отсылки статусов, сообщений да и вообще. Не будем отказывать:
–C:

[codesyntax lang=”xml”]<iq type="set" id="9747">
<session xmlns="urn:ietf:params:xml:ns:xmpp-session" />
</iq>[/codesyntax]

Сессия успешно создана:
–S:

[codesyntax lang=”xml”]<iq type=’result’ id=’9747′>
<session xmlns=’urn:ietf:params:xml:ns:xmpp-session’/>
</iq>[/codesyntax]

Рассылка статуса “доступен”

presence – для получения и рассылки широковещательной информации (напр. информация о статусе), а так же информации о подписке (например, добавление юзера в контакт-лист). Хотя может включать и указатель to для отсылки данных только определенному пользователю (напр. для определенного контакта ростера выставить статус “не беспокоить”:])
Может иметь атрибуты
from – JID имя@сервер/ресурс от кого presence. В принципе можно и не ставить – проставит сервер.
to – JID (с ресурсом или без) кому предназначен данный presence. Если не указано, рассылается всем, кто подписан (контакт-листу etc.)
type – тип presence, если отсутствует – то простое сообщение о статусе, иначе один из списка: unavailable (отключен),probe (запрос статуса клиента с сервера), subscribe (запрос подписки/просьба добавить в ростер), unsubscribed (отозвать подписку/удалить из ростера), subscribed (одобрить подписку/добавить в ростер)

Отсылаем сообщение об изменении статуса:
–C:

[codesyntax lang=”xml”]<presence>
<!– Выставляем приоритет для этого ресурса (сообщения приходят ресурсу с высшим из подключенных или сразу всем, если приоритеты одинаковые) –>
<priority>50</priority>
<!– Стандартный статус, если отсутствует – то "Доступен" либо один из списка xa (eXtended Away недоступен), away (отошел), chat (готов поболтать), dnd (занят – не беспокоить) –>
<show>chat</show>
<!– Капс, что-то вроде идентификатора софта клиента –>
<c xmlns="http://jabber.org/protocol/caps" node="http://gajim.org/caps" ext="xhtml cstates" ver="0.11.4" />
<!– Расширенный текстовый статус –>
<status>Ааа ппц я в консоли</status>
<!– Обновленная картинка x-статуса (ее sha1-хеш) –>
<x xmlns="vcard-temp:x:update">
<photo>b2730e40aba4f7225456d0b4789bf2d5af34c3e3</photo>
</x>
</presence>[/codesyntax]

Хочу заметить, что ни один из вложенных параметров не обязателен. Можно отправить один <priority></priority> или только <status></status> … Что хотим изменить то и отсылаем.

В ответ сервер рассылает всем из твоего контакт-листа (точнее тем, кто подписался командой prescence type=”subscribe” в т.ч. иногда и тебе) сообщение
–S:

[codesyntax lang=”xml”]<presence from=’seriy.pr@jabber.ru/telnet’ to=’seriy.pr@jabber.ru/telnet’>
<priority>50</priority>
<show>chat</show>
<c xmlns="http://jabber.org/protocol/caps" node="http://gajim.org/caps" ext="xhtml cstates" ver="0.11.4" />
<status>Ааа ппц я в консоли</status>
<x xmlns="vcard-temp:x:update">
<photo>b2730e40aba4f7225456d0b4789bf2d5af34c3e3</photo>
</x>
</presence>[/codesyntax]

Отправка/получение сообщений

message-для отсылки и получения сообщений. Имеет атрибуты
to адресат – JID (с ресурсом или без)
from отправитель – JID с ресурсом (чужой подставить не получится:P)
type тип сообщения – один из списка: chat – сообщение тет-а-тет чата (как ICQ, обычно используется по-умолчанию), normal – одиночное сообщение, откроется в отдельном окошке, на него можно ответить таким-же сообщением, groupchat (сообщение в конференции), headline (сообщения, отсылаемые автоматическими оповещалками, RSS-ботами и т.п., откроется в отдельном окошке, на него нельзя ответить), error – сообщение об ошибке, отображается в окне чата в особым образом отформатированном виде
Отправим чат-сообщение автору статьи:
–C:

[codesyntax lang=”xml”]<message to="seriy.pr@jabber.ru" from="user1@jabber.ru/telnet" type="chat">
<!– Собственно, само тело сообщения –>
<body>Тук-тук!</body>
<!– Тут может содержаться информация о том, что автор сообщения "печатает текст"/"прекратил печатать"/"закрыл окошко" и т.п., это вам на домашнее задание –>
</message>[/codesyntax]

(попугайте друзей – отправьте сообщение с типом error)

Рассылка статуса “отключен”

Длч правильного завершения сеанса обычно рассылается статусное сообщение “отключен” (type=”unavailable”)
–C:

[codesyntax lang=”xml”]<presence xmlns="jabber:client" type="unavailable" />[/codesyntax]

Закрытие потока

Закрываем поток такой командой
–C:

[codesyntax lang=”xml”]</stream:stream>[/codesyntax]

И сервер тоже закрывает поток
–S:

[codesyntax lang=”xml”]</stream:stream>[/codesyntax]

Отключение от сервера

Соединение будет закрыто сразу после закрытия потока по инициативе сервера.

Вот  в общем-то и все.

В продолжении хотелось-бы рассмотреть:

  • Аутентификация методом DIGEST-MD5 (если сам разберусь)
  • Включение шифрования канала (тут изменения будут небольшие)
  • Работа с ростером
  • Работа с конференциями
  • Работа с “визитной карточкой”
  • Обзор сервисов
  • Работа с транспортами (м/б)

Литература:

http://tools.ietf.org/html/rfc3920 rfc3920 XMPP ядро протокола
http://tools.ietf.org/html/rfc3921 rfc3921 XMPP статусы, сессии и т.п.
http://tools.ietf.org/html/rfc2831 rfc2831 SASL аутентификация
http://www.bog.pp.ru/work/SASL.html немного о SASL по-русски
http://ru.wikipedia.org/wiki/XMPP XMPP в вики
http://ru.wikipedia.org/wiki/Jabber Jabber в вики

14 thoughts on “Протоколы прикладного уровня: Jabber/XMPP часть1

  1. vlad

    Это можно оправлять/получать, импользуя ms-клинет telnet?!

    Reply
    1. P.S. Post author

      Спасибо.
      Рад что пригодилось! В принципе по ссылкам на спецификацию можно остальное самостоятельно разобрать. А продолжения нет, т.к. для меня неактуально стало.

      Reply
      1. 000

        Не могли бы Вы подсказать, где можно почитать, желательно по-русски, о запросах при общении в конференциях jabber?

        Reply
          1. P.S. Post author

            Спасибо за дополнения.
            На русском языке я почти не смог найти информации по XMPP, все на английском.
            Вообще, почти все сообщения в Jabber – это XML теги <presence/> <message/> и <iq/>, главное – разобраться в их атрибутах и порядке, в котором они посылаются)))

  2. Марк

    Спасибо, статья пригодилась, могу дополнить работу со списком контактов, добавление пользователей долго искал:
    список контактов

    добавление:

    Reply
    1. Марк

      не прикрепилось:
      [codesyntax lang=”xml”]

      [/codesyntax]
      это запросы с клиента

      Reply
      1. P.S. Post author

        Мда, проблемы с разметкой… опробуйте в тег <pre> завернуть

        Reply
        1. Марк

          Извиняюсь, что создал два бессмысленных комментария, надеюсь в этом отобразиться:
          список контактов:

          
          <!-- список контактов:-->
          <iq type="get" id="id_запроса">
               <query xmlns="jabber:iq:roster"></query>
          </iq>
          

          добавление в список контактов:

          
          <!-- добавление в список контактов:-->
          <iq xmlns="jabber:client" type="set"  id="id_запроса">
              <query xmlns="jabber:iq:roster">
                   <item jid="id_добавляемого_пользователя"></item>
               </query>
          </iq>
          

          на всякий случай http://pastebin.com/LX72XJS7
          конечно этой статье уже пять лет, и XMPP скорее мертв, чем жив, но надеюсь, что кому-то пригодится

          Reply
          1. P.S. Post author

            Спасибо! Подредактировал ваш комментарий. Почему-то wordpress все теги вырезает.

Leave a Reply to 000 Cancel reply

Your email address will not be published. Required fields are marked *