Блог - Linux, программирование, Я!

ОбщекомпьютерноеПротоколы прикладного уровня: 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 в вики

  1. vlad
    2009-03-31 18:57:09 | #

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

  2. 2009-04-08 17:36:38 | #

    честно говоря, не пробовал… Но теоретически можно.

  3. 2009-11-10 14:06:15 | #

    Спасибо некоторые моменты полезны

  4. 000
    2010-06-19 20:52:06 | #

    Спасибо, пригодилось. Жаль, нет продолжения.

    • 2010-06-19 21:19:27 | #

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

      • 000
        2010-06-21 13:21:19 | #

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

        • 000
          2010-06-21 16:25:30 | #

          Отвечу сам на свой вопрос):
          http://xmpp.org/extensions/xep-0045.html — суровая база. Мне лично разобраться было трудно.
          http://commons.oreilly.com/wiki/index.php/JabChapter_9 — Тут попонятней. Хотя и намного короче.

          • 2010-06-21 20:45:05 | #

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

  5. 2011-03-10 19:02:23 | #

    Хорошая статья, пригодится для моих проектов 🙂

  6. Марк
    2014-11-27 00:37:34 | #

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

    добавление:

    • Марк
      2014-11-27 00:40:32 | #

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

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

      • 2014-11-27 02:06:52 | #

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

        • Марк
          2014-11-27 02:15:01 | #

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

                                          
          <!-- список контактов:-->
          <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 скорее мертв, чем жив, но надеюсь, что кому-то пригодится

          • 2014-11-27 04:06:00 | #

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