Предварительный разбор по 1С
- Поддержка XML у 1С реализована хорошо, а протокол XMPP - это чистый XML.
- XMPP сервер работает с клиентами через сокеты/потоки, на 1С реализовать это уже сложнее, но работу с сервером XMPP можно расширить через модули для работы по HTTP (BOSH - http-bind).
- Работа с Base64 - реализован в 1С через команды: Base64Значение и Base64Строка.
- Расчет хэш-суммы по алгоритму MD5, реализован в 8.3 объектом ХешированиеДанных.
Разбор по XMPP
Простая сессия представляет из себя следующую последовательность операций:
- Соединение с сервером
- Создание потока
- Аутентификация
- Привязывание (bind) потока к ресурсу (имя@сервер/ресурс)
- Создание сессии
- Рассылка статуса "доступен"
- Отправка/получение сообщений, статусы, ростер(список контактов), "визитные карточки", работа с сервисами и транспортами и т.п.
- Рассылка статуса "отключен"
- Закрытие потока
- Отключение от сервера
В качестве примера (он будет достаточен), реализуем самый минимум и заморачиваться с ростером, закрытием сессий - не будем. Самое последнее действие, которое реализеум - это просто отправка сообщения. Оговоримся сразу, в примере буду использовать сервер jabber.ru, а ники вымышленные.
1 - Приветствие, получение кода сессии SID:
<body
content='text/xml; charset=utf-8'
from='myNickXMPP@jabber.ru'
secure='true'
hold='1'
rid='678901'
to='jabber.ru'
route='http://jabber.ru/http-bind'
ver='1.10' wait='300'
xml:lang='en' xmpp:version='1.0'
xmlns='http://jabber.org/protocol/httpbind'
xmlns:xmpp='urn:xmpp:xbosh'/>
2 - Отправка серверу команды на авторизацию методом DIGEST-MD5:
<body rid='678902' sid='1234567890' xmlns='http://jabber.org/protocol/httpbind' xmlns:xmpp='urn:xmpp:xbosh'>
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>
</body>
3 - Авторизация методом DIGEST-MD5:
<body rid='678903' sid='1234567890' xmlns='http://jabber.org/protocol/httpbind'>
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
МАССИВ ДАННЫХ АВТОРИЗАЦИИ ЗАКОДИРОВАННЫХ BASE64
</response>
</body>
4 - После успешной авторизации, отправляем встречный ответ подтверждение:
<body rid='678904' sid='1234567890' xmlns='http://jabber.org/protocol/httpbind'>
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
</body>
5 - Повторно посылаем приветствие и команду "рестарта":
<body
rid='678905'
sid='1234567890'
to='jabber.ru'
xml:lang='en'
xmpp:restart='true'
xmlns='http://jabber.org/protocol/httpbind'
xmlns:xmpp='urn:xmpp:xbosh'/>
6 - Устанавливаем имя ресурса (отправим пустой):
<body rid='678906' sid='1234567890' xmlns='http://jabber.org/protocol/httpbind'>
<iq id='bind_1' type='set' xmlns='jabber:client'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
</iq>
</body>
7 - Затем сервер требовал создать сессию... Это нужно для отсылки статусов, сообщений, да и вообще. Не будем отказывать:
<body rid='678907' sid='1234567890' xmlns='http://jabber.org/protocol/httpbind'>
<iq id='bind_2' type='set' xmlns='jabber:client'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session' />
</iq>
</body>
8 - Ну и теперь выходим в онлайн и становимся видимыми для ваших контактов:
<body rid='678908' sid='1234567890' xmlns='http://jabber.org/protocol/httpbind'>
<presence>
<show/>
<status>1c input</status>
<priority>10</priority>
</presence>
</body>
Одна из возникших проблем: на данный момент, мне не удалось отправить из 1С статус на русском языке, и таже самая проблема при отправке сообщения.
9 - Отправка сообщения другому пользователю:
<body rid='678909' sid='1234567890' xmlns='http://jabber.org/protocol/httpbind'>
<message to='poluchatel@jabber.ru' from='myNickXMPP@jabber.ru' xmlns='jabber:client' type='chat'>
<body>test message</body>
</message>
</body>
Как видите ничего сложного - нет, рассматривать ответ от сервера мы тут не будем, этого в инете достаточно. Рабочий код(пример) выложен ниже.
Разбор полёта
Обработка соединяется с сервером и отсылает сообщение, но так как это всего-навсего пример и его цель только показать, что ничего невозможного сейчас на 1С - нету, то есть недоработки и корявости(сырости) в реализации.
Хотел бы обратить внимание (на двух) уже на одной проблеме, которой пока не нашел решение(не забываем, что реализация на чистом 1С, без COM). Без решения данной проблемы - пример всёравно работает.
Как сказано выше, не удалось отправить сообщение и статус на кириллице, поэтому отсылаем только на латинице.Генератор случайных чисел - работает коряво и часто вылетает по ошибке, поэтому вставил уже сгенерированый и закодированный случайны код - oUIOt7lGFY0Q3Z+/1qo0j9lhpB1MscW9Fu4MJM/nNhc=.
Вся сложность взаимодействия с XMPP из 1С, это создания ключа авторизации по особому способу:
Функция строкаРеспонсе(структураДляОтвета)
// функция создания поля response для challenge
СтрокаВозвр="";
authzid = Ложь;
authzid_знач = "";
Для каждого првСтрк из структураДляОтвета Цикл
Если првСтрк.Ключ = "authzid" Тогда
authzid=Истина;
authzid_знач = првСтрк.Значение;
КонецЕсли;
КонецЦикла;
realm="";
Если НЕ структураДляОтвета.realm="-" Тогда
realm=структураДляОтвета.realm;
КонецЕсли;
раскНЕХ64 = мд5бз64(Объект.Пользователь+":"+realm+":"+Объект.Пароль);
Сообщить(раскНЕХ64);
а1 = ":"+структураДляОтвета.nonce+":"+структураДляОтвета.cnonce;
Если authzid Тогда
а1 = а1+":"+authzid_знач;
КонецЕсли;
а2="AUTHENTICATE:"+структураДляОтвета.digest_uri;
общаяСтрока = ""+мд5(а1,раскНЕХ64)+
":"+ структураДляОтвета.nonce +
":"+ структураДляОтвета.nc +
":"+ структураДляОтвета.cnonce +
":"+ структураДляОтвета.qop +
":"+ мд5(а2);
Сообщить("rD="+общаяСтрока);
СтрокаВозвр = мд5(общаяСтрока);
Возврат СтрокаВозвр;
КонецФункции
Функция СлучайноеЧисло(Мин,Макс,флЦелое = 0)
ГСЧ = Новый ГенераторСлучайныхЧисел();
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(1, 12000);
//вместо Randomize
Для н = 1 По СлучайноеЧисло Цикл
Уник = Новый УникальныйИдентификатор;
КонецЦикла;
//генерируем GUID
Уник = СокрЛП(Новый УникальныйИдентификатор);
//оставляем только цифры
Уник = СтрЗаменить(Уник,"-","");
Уник = СтрЗаменить(Уник,"a","");
Уник = СтрЗаменить(Уник,"b","");
Уник = СтрЗаменить(Уник,"c","");
Уник = СтрЗаменить(Уник,"d","");
Уник = СтрЗаменить(Уник,"e","");
Уник = СтрЗаменить(Уник,"f","");
//знаменатель должен иметь такое же количество нулей + 1
Знаменатель = 10;
Для н = 2 По (СтрДлина(СтрЗаменить(Уник,Символы.НПП,""))) Цикл
Знаменатель = Знаменатель * 10;
КонецЦикла;
Случ = (Число(Уник)) / Знаменатель; //здесь получается дробное случайное число от 0 до 1
//преобразуем его в случайное число из заданного интервала, округляем до целого
ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс);
Возврат ЧислоИзИнтервала;
КонецФункции
Функция _2()
//--//--//--
Если узл.ИмяУзла = "challenge" Тогда
неХЭШстрока = расшф64(узл.ТекстовоеСодержимое);
струк = строк_струк(неХЭШстрока);
Если струк.количество()>0 Тогда
// нормально разобрали, теперь добавляем доп.параметры
// 1-добавление к challenge digest-uri если он не пришел в ответе от сервера
digest_uri = Ложь;
Для каждого првСтрк из струк Цикл
Если првСтрк.Ключ = "digest-uri" Тогда
digest_uri=Истина;
КонецЕсли;
КонецЦикла;
Если НЕ digest_uri Тогда
струк.Вставить("digest_uri", "xmpp/"+Объект.Домен);
КонецЕсли;
// 2-генерация cnonce - уникальный номер сессии
гуид = Новый УникальныйИдентификатор();
уникномерсесс = СтрЗаменить(гуид,"-","");
струк.Вставить("cnonce", зашф64(уникномерсесс));
струк.Вставить("nc", "00000001");
// 3-проверка в challenge -> qop пришел в ответе от сервера и правильный ли он
qop = Ложь;
Для каждого првСтрк из струк Цикл
Если првСтрк.Ключ = "qop" Тогда
qop=Истина;
КонецЕсли;
КонецЦикла;
Если НЕ qop Тогда
струк.Вставить("qop", "auth");
Иначе
Если НЕ струк.qop = "auth" Тогда
струк.qop = "auth";
КонецЕсли;
КонецЕсли;
// 3-добавление к challenge realm если он не пришел в ответе от сервера
realm = Ложь;
Для каждого првСтрк из струк Цикл
Если првСтрк.Ключ = "realm" Тогда
realm=Истина;
КонецЕсли;
КонецЦикла;
Если НЕ realm Тогда
струк.Вставить("realm", "-"); // надо учитывать что
КонецЕсли;
КонецЕсли;
Если струк.realm = "-" Тогда
Знач_realm="";
Иначе
Знач_realm=струк.realm;
КонецЕсли;
// 4-создание кодированной строки response на основании некоторых данных из challenge и логина-пароля
респонсеСтр=строкаРеспонсе(струк);
// формируем строку для запроса №3
строкаВозвратаДля3 =
"username="""+Объект.Пользователь+""","+
"realm="""+Знач_realm+""","+
"nonce="""+струк.nonce+""","+
"cnonce="""+струк.cnonce+""","+
"nc="+струк.nc+","+
"qop="+струк.qop+","+
"digest-uri="""+струк.digest_uri+""","+
"response="+респонсеСтр+","+
"charset=utf-8";
Объект.challenge_actv=Истина;
КонецЕсли;
//--//--//--
КонецФункции
P.S. Есть пару идей использования протокола: можно организовать свой домашний сервер ботов на 1С, который может обрабатывать некую информацию и результат обработки отсылать пользователю в том-же xml или json. Например: по заказу сделали отчет или некую конфу, закодировали часть кода и ключ раскодирования отдавать пользователю по его запросу; можно также ограничить по количеству запусков, не более 10 раз и хватит с клиента. Причем, серверную часть своего бота можете ораганизовать на любом языке (не только 1С). Приложить свои усилия по доработке можно на github.