вторник, 21 сентября 2010 г.

Пишем Jabber-клиент на Delphi. Часть 5

Статусы, состояния, информация о присутствии, управление подпиской <Presence>

Прием и отправка статусных сообщений, а также информации о видимости  контактов и подписки на сообщения от них, осуществляется через XML-строфу <Presence>.

Атрибут type строфы <Presence> является дополнительным.

Строфа, которая не обладает атрибутом type, используется Jabber-ом, для сообщений о присутствии контакта в сети Jabber и указывает на то, что данный контакт находится в сети (онлайне) и доступен для коммуникации.

Если атрибут type присутствет в строфе <Presence>, то он управляет подпиской на сообщения и смену статусов другого контакта (объекта). Аналог подписки в IM-сетях является прохождение авторизации в ICQ.

Если атрибут включен, то он должен содержать иметь одно из следующих значений:,
unavailable Сигнализирует, что данный контакт, больше не доступен для      коммуникаций. Фактически контакт вышел в оффлайн.
subscribe Запрос на подписку (авторизацию) от другого контакта.
subscribedИнформирует о том, что контакт разрешил авторизацию.
unsubscribe - Отправитель аннулирует подписку.
unsubscribed Запрос на аннулирование подписки (отозвание авторизации) от другого контакта.
probe - Запрос о текущем присутствии контакта только сервером от имени пользователя.
error Ошибка, произошедшая при доставки предыдущих данных. Обработка такого сообщения идет в соответствии с RFC 3920 XMPP-Core.
Например, запрос на подписку от контакта ivanov@jabber.ru для нашего контакта может выглядеть так:
     <presence to="delphi-test@jabber.ru" type="subscribe"
           from="ivanov@jabber.ru"/>
     </presence>
Разрешение авторизации в ответ :
     <presence to="ivanov@jabber.ru" type="subscribed"/>
А запрет (отказ) вот так:
     <presence to="ivanov@jabber.ru" type="unsubscribed"/>

Дочерние элементы

XML-строфа <Presence>  может содержать следующие дочерние элементы, определенные пространством имен 'jabber:client' или 'jabber:server': <show>,  <status>, <priority>. По умолчанию за этими элементами зарезервировано пространство имен 'jabber:client'. Если <Message> имеет тип error то обработка такого сообщения идет в соответствии с RFC 3920 XMPP-Core.
Элемент <show> определяет статус  контакта и может иметь следующие значения:
away – Отошел,
chat – Готов чатится (В сети),
dnd – Занят,
xa - Недоступен
Пустой элемент <show/> определяет статус  контакта  “ В сети ”.
Элемент <status> определяет статусное сообщение. Значением элемента является строка с текстом сообщения, например:
<presence>
 <status>Смотрю фильм</status>
</presence>
Необязательный элемент <priority> определяет приоритет уровня ресурса. Значением элемента является число от -128 до 127.

Информационные запросы <IQ>

Информационные запросы <IQ> разделяются на стандартные, определенные пространством имен 'jabber:client' или 'jabber:server', обеспечивающие базовые функциональные возможности и расширенные.
Расширенные запросы, определенные дополнительными пространствами имен, описаны в различных дополнениях к протоколу XMPP.
Пространство имен расширенных запросов, может содержать любое значение, кроме зарезервированных следующих пространств имен: "jabber:client", "jabber:server" или   "http://etherx.jabber.org/streams". Такое расширение позволяет придать протоколу XMPP дополнительную функциональность  и гибкость. Таким образом, расширенный информационный запрос <IQ> может содержать один или более дополнительных дочерних элементов, определяющих информационное наполнение, которое расширяет значение данного запроса. Это кардинальное отличие от стандартного информационного запроса.
Стандартный запрос не может содержать, дочерние элементы, кроме элемента <error>. Наличие данного элемента в запросе показывает наличие ошибки.
Поддержка любого расширенного пространства имен является дополнительной возможностью со стороны клиента. Если клиент не понимает такое пространство имен, то есть фактически не поддерживает данное расширение, то он должен проигнорировать данный пакет. Более подробно вы можете прочитать об этом в RFC 3921.
Структурная схема обмена информационными запросами:

    Запрос                       Ответ

   ----------                 ----------

       |                           |

       | <iq type='get' id='1'>    |

       | ------------------------> |

       |                           |

       | <iq type='result' id='1'> |

       | <------------------------ |

       |                           |

       | <iq type='set' id='2'>    |

       | ------------------------> |

       |                           |

       | <iq type='error' id='2'>  |

       | <------------------------ |

       |                           |

Как мы видим, на структурной схеме обмен между клиентами происходит по такому алгоритму. Запрашивающая сторона посылает <IQ> запрос с атрибутом type равным значению “get”. Данный атрибут на принимающей стороне говорит клиенту, что вы должны предоставить информацию по данному запросу (для расширенных запросов при условии, что он поддерживается клиентом). Принимающая сторона отправляет ответ с атрибутом с атрибутом type равным значению “result”.
Это первый вариант обмена. Существует и второй, когда запрашивающая сторона информирует принимающую о каком-то изменении, для этого она отправляет запрос с атрибутом type равным значению “set”. Данное значение атрибута говорит о том, что принимающая сторона должна обработать присланные данные. Если принимающая сторона не может по каким-либо причинам обработать присланные данные, то в ответ она посылает строфу <IQ> с атрибутом type равным значению “error” информируя запрашивающую сторону о невозможности обработки. Если принимающая сторона корректно обработала запрос c атрибутом “set” то она возвращает ответ с атрибутом равным значению “result”.
Пример расширенного запроса определяющий информацию об использованном клиенте (XEP-0092 Software Version):
Запрос:
<iq from='delphi-test2@jabber.ru/QIP'
   to='delphi-test@jabber.ru/тестовая'

    xml:lang='ru' type='get' id='qip_30'>

      <query xmlns='jabber:iq:version'/>

</iq>

Ответ:

<iq type='result' to='delphi-test2@jabber.ru/QIP'
    from='delphi-test@jabber.ru/тестовая'
    id='qip_30'>

       <query xmlns='jabber:iq:version'>

          <name>Мой клиент</name>

          <version>0.5.0.1</version>

       </query>

</iq>

Работа с ростер-листом (списком контактов)

Ростер-лист или аналог списка контактов в сетях ICQ в Jabber-е представлен списком, содержащим JID-контакты в виде элементов XML хранящимся на сервере от имени пользователя. Так как ростер-лист сохранен сервером от имени пользователя, то пользователь может обратиться к информации списка от любого ресурса.

Управление ростер-листом (списком) осуществляется через расширенный информационный запрос <IQ> содержащий дочерний элемент <query> c пространством имен 'jabber:iq:roster'. Элемент <query> может содержать один или более дочерних элементов <ITEM> содержащих информацию о контакте.

Уникальный идентификатор каждого элемента списка <item> - это JID контанта,   формируемый в атрибуте jid  Значение атрибута jid имеет форму   user@domain без указания ресурса. Текущее состояние подписки пользователя (контакта) относительно элемента <item> зафиксировано в атрибуте subscription и может принимать следующие значения:

none - У пользователя нет подписки к контакту, нет подписки и к информации присутствия пользователя

to - у пользователя есть подписка к информации присутствия контакта, но у контакта нет подписки к информации присутствия пользователя

from - у контакта есть подписка к информации присутствия пользователя, но у пользователя нет подписки к информации присутствия контакта

both – у пользователя есть подписка к присутствию контакта, да и у контакта есть подписка к пользователю.

Запрос списка контактов при входе в систему

При входе в систему клиент Jabber должен послать серверу информационный запрос о получении ростер-листа.

Запрос ростер-листа клиентом:

<iq from='delphi-test@jabber.ru/тестовая'
     type='get' id='roster_1'>

     <query xmlns='jabber:iq:roster'/>

</iq>

Получение ростер-листа с сервера:

<iq from='delphi-test@jabber.ru'
     to='delphi-test@jabber.ru/тестовая' id='roster_1'
     type='result'>
     <query xmlns='jabber:iq:roster'>
          <item subscription='from'
                name='
Тест 2'
                jid='delphi-test2@jabber.ru'/>
     </query>
</iq>

Управление ростер-листом

Добавление или редактирование контакта. При отсутствии контакта в ростер-листе контакт будет добавлен, при наличии отредактирован.
Добавление / корректировка. Клиент посылает следующий пакет.
   <iq from='delphi-test@jabber.ru/тестовая'
    
type='set' id='уникальный номер'>

     <query xmlns='jabber:iq:roster'>

       <item jid='новый/корректируемый JID'

             name='Имя контакта'>

         <group>Группа контакта</group>

       </item>

     </query>

   </iq>

После добавления/обновления информации о контакте на сервере, сервер оповещает все доступные ресурсы пользователя о внесенной информации. Оповещение служит сигналом для синхронизации данных клиентов о данном контакте с данными сервера.
Оповещение сервера:
   <iq to='delphi-test@jabber.ru/тестовая'
       type='set'

       id='уникальный номер'>

     <query xmlns='jabber:iq:roster'>

       <item jid='новый/корректируемый JID'

             name='Имя контакта'>

         <group>Группа контакта</group>

       </item>

      </query>

   </iq>

Информация о результате:
<iq to='delphi-test@jabber.ru/тестовая' type='result' id='уникальный номер'/>
   
В любое время, пользователь удалить контакт из ростер-списка, для этого клиент должен послать запрос с атрибутом subscription элемента <item> равным значению 'remove' :
<iq from=' delphi-test@jabber.ru/тестовая' type='set'      id='roster_4'>

<query xmlns='jabber:iq:roster'>

 <item jid='JID который удаляется' subscription='remove'/>

</query>

</iq>
Как и в случае с добавлением/корректировкой контакта сервер оповещает клиенты о удалении контакта. Указанием факта удаления служит атрибут subscription равным значению 'remove' в элементе <item>.
<iq to=' delphi-test@jabber.ru/тестовая' type='set' id='roster_4'>

 <query xmlns='jabber:iq:roster'>

  <item jid='JID который удален сервером' subscription='remove'/>

 </query>

</iq>

Заключение

Как вы видите, ничего особо сложного нет. Простой Jabber-клиент с минимальной функциональностью представлен в примере http://zalil.ru/29720936 (к сожалению я пока не разобрался как выкладывать файлы в блог, поэтому ссылку даю на внешний файлообменник)

7 комментариев:

  1. а собственно исходники модно в студию?

    ОтветитьУдалить
  2. Это не "Пишем Jabber-клиент". Это "разбираем протокол XMPP".
    На пять уроков одна функция и море информации о протоколе

    ОтветитьУдалить
  3. Дал ссылку на исходники простого Jabber-клиента(версия для D2010)

    к сожалению я пока не разобрался как выкладывать файлы в блог, поэтому ссылку даю на внешний файлообменник

    ОтветитьУдалить
  4. Добрый день, выложите пожалуйста свежую ссылку на исходник. Заранее благодарен

    ОтветитьУдалить
  5. Обновите пожалуйста ссылку на исходники, очень хочется посмотреть.

    ОтветитьУдалить
  6. Обновите пожалуйста ссылку на исходники, пишу программу - нужна SASL авторизация.

    ОтветитьУдалить