Описание

Данный сервис позволяет поддерживать удаленную книгу Yealink, GrandStream, Cisco и ТА других производителей которую возможно получать либо из базы абонентов SSW (по http|https), либо по протоколу ldap, либо с адресной книги ECSS-10 (используя mysql).
На текущий момент:

  • Из базы абонентов ssw можно получить: имя и номер;
  • По протоколу ldap можно получить: имя, номер, почту;
  • Используя mysql: имя, номер, почту(почта только по протоколу CardDAV ).

Конфигурация сервиса

Настройка сервиса

Заранее нужно определиться из какого источника будет осуществляться получение данных абонентов.

  • Если данные будут получены по ldap, необходимо чтобы на момент настройки был доступ к развернутому ldap серверу (сторонний сервер).
  • Если абонентские номера будут получены через mysql, то необходимо иметь доступ до сервера mysql, который используется ECSS-10 (по умолчанию при установке пакета ecss-mysql создается специальный пользователь address_book с доступом до таблицы address_book). При этом, если будет использован другой пользователь mysql, необходимо убедиться, что у него есть доступ до базы address_book.
  • Если номера абонентов будут получены через ssw, необходимо иметь доступ до http терминала сервера. 

При установке пакета ecss-restfs необходимо указать следующие ответы:

  • На вопрос "Необходимо ли настроить телефонную книгу?" ответить положительно ("Да");
  • Далее в соответствии с сервисом/сервисами которые будут использоваться для формировании адресной книги заполнить параметры доступа; Список вопросов при инсталляции пакетов приведен в разделе "Инсталляция системы".

Если на момент настройки сервиса пакет ecss-restfs уже был установлен, используйте команду переконфигурирования сервиса:

$ sudo dpkg-reconfigure ecss-restfs
CODE

Сервис можно временно сконфигурировать  и через файл /usr/lib/ecss/ecss-restfs/conf/address-book/card-settings.json.

Пример конфигурационного файла:

{
  "ldap": {
    "base_name": "cn=admin,dc=eltex,dc=com",
    "password": "",
    "domain": "ou=users,dc=eltex,dc=com",
    "server": "localhost",
    "attrs": [
      "mail",
      "telephoneNumber",
      "displayName"
    ],
    "translit": "false",
    "scope": "subtree"
  },
  "ssw": {
    "protocol": "https",
    "server": "localhost:9999",
    "user": "admin",
    "password": "password",
    "domain": "biysk.local",
    "translit": "false",
    "limit": "10000"
  },
  "mysql": {
    "server": "address-book.mysql.ecss",
    "port": "3306",
    "user": "address_book",
    "password": "address_book",
    "db": "ecss_address_book",
    "domain": "biysk.local",
    "procedure": "getContactBook",
    "limit": "10000",
    "translit": "false",
    "offset": "0"
  },
  "carddav": {
    "backend": "ECSS-Restfs-vcard",
    "displayname": "",
    "protocol": "http",
    "server": "localhost",
    "port": "9990",
    "args": "",
    "description": "Address book ECSS"
  },
  "global": {}
}

CODE

Изменив параметры в файле необходимо перезагрузить сервис ecss-restfs.

$ sudo systemctl restart ecss-restfs
CODE

Однако настройки, выставленные в этом файле будут сброшены после обновления пакета ecss-restfs. Верная схема настройки сервиса через установку или реконфигурирование пакета ecss-restfs.

Шаблоны

Шаблоны для формирования адресных книг для телефонов различных производителей должны находится по пути /etc/ecss/ecss-restfs/template.

Имя каждого шаблона должно соответствовать следующему виду: -<tempate_name>.xml, где <tempate_name> — имя шаблона. 

В текущей версии в данном каталоге уже имеются готовые шаблоны для ТА Cisco, GrandStream, стандартный шаблон common.xml(подойдет для ТА Yealink, Eltex VP). Также есть шаблон для выгрузки книги в формате vcard(используют мобильные приложения).

Шаблон common.xml

<{{context.vendor}}IPPhoneDirectory>
    <Title>Restfs address book</Title>
    <Prompt>Prompt text.</Prompt>
{% for id, person in ipairs(context.data) do %}
     <DirectoryEntry>
        <Name>{{string.strip(trim(person.name), context.param.name_len)}}</Name>
    {% for tag, val in pairs(person.phone) do %}
        <Telephone>{{trim(val)}}</Telephone>
    {% end %}
     </DirectoryEntry>
{% end %}
</{{context.vendor}}IPPhoneDirectory>

CODE

Шаблон grandstream.xml

<?xml version="1.0" encoding="UTF-8"?>
<AddressBook>
    <version>1</version>
  {% for id, person in ipairs(context.data) do %}
    <Contact>
        <FirstName searchName="{{person.name}}">{{person.name}}</FirstName>
        <MiddleName searchName="{{person.middle_name}}">{{person.middle_name}}</MiddleName>
        <LastName searchName="{{person.last_name}}">{{person.last_name}}</LastName>
        <!-- phone -->
        {% for tag, val in pairs(person.phone) do %}
        <Phone>
            <phonenumber>{{val}}</phonenumber>
            <accountindex>{{id}}</accountindex>
        </Phone>
        {% end %}
        <!-- phone end -->
        <Group>0</Group>
        <PhotoUrl/>
        <RingtoneUrl>./</RingtoneUrl>
        <RingtoneIndex>0</RingtoneIndex>
        <!-- mail -->
        {% for _, val in ipairs(person.email) do %}
        <Mail>{{val}}</Mail>
        {% end %}
        <!-- mail end -->
    </Contact>
      {% end %}
</AddressBook>
CODE

Шаблон vcard.vcf

{% for id, person in ipairs(context.data) do %}
BEGIN:VCARD
VERSION:4.0
PRODID:-//ECSS RESTFS//Carddav 3.11//EN
UID:{{ ngx.md5(person.name .. id):gsub("%a", "") }}
CATEGORIES: {{person.domain}}
FN:{*person.name*}
N:{*person.last_name*};{*person.first_name*};{*person.middle_name*};;
{% for _, val in ipairs(person.email) do %}
EMAIL;TYPE=INTERNET:{*val*}
{% end %}
{% for tag, val in pairs(person.phone) do %}
TEL;TYPE=WORK:{*val*}
{% end %}
REV:{{ ngx.localtime() }}
END:VCARD
{% end %}
CODE

На основе данных примеров можно создавать пользовательские шаблоны для ТА или приложений разных вендоров. Структура файла для загрузки адресной книги как правило приводится в документации к ТА.

Чтобы телефон мог воспользоваться заданным шаблоном, необходимо, чтобы в запросе присутствовал параметр user_agent=<tempate_name>, например:

http://192.168.1.21:9990/mysql?host=book&user_agent=grandstream
CODE

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

Если шаблон не обнаружен, то возвращается адресная книга по стандартному шаблону.

Использование сервиса

Общая схема запроса

Для получения адресной книги запрос должен соответствовать следующему синтаксису:

Таблица 1

Синтаксис запроса

http://<IP>:9990/<service>?host=book[&<key>=<value>][]
CODE

Параметры:

  • <IP> — ip-адреса сервера, где установлен пакет ecss-restfs;
  • <service> — сервис используемый для получения телефонной книги (mysql | ldap | ssw);
  • <key> — дополнительные опциональные параметры запроса, порядок не имеет значения(добавляются к запросу через символ &):
    • domain — имя домена для которого забирается адресная книга: str;
    • name_len — максимальная длина имени: number > 0;
    • skip_no_disp — пропускать записи без имени: true|false;
    • translit — использовать транслит: true|false;
    • user_agent — имя шаблона для адресной книги: str;
    • limit — ограничение на количество записей: number > 0.
  • <value> - значение параметра

Если информация по запросу существует, возвращается код 200, а в теле ответа - телефонная книга. При возникновении ошибки причину можно посмотреть в файлах /var/log/ecss/restfs/error-card-*.log.

Пример:

Запрос:

curl 'http://192.168.1.21:9990/mysql?host=book&domain=biysk.local' -v
CODE

Ответ:

*   Trying 192.168.1.21:9990...
* TCP_NODELAY set
* Connected to 192.168.1.21 (192.168.1.21) port 9990 (#0)
> GET /mysql?host=book&domain=biysk.local HTTP/1.1
> Host: 192.168.1.21:9990
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.14.0 (Ubuntu)
< Date: Thu, 26 Aug 2021 04:34:04 GMT
< Content-Type: text/xml; charset=utf-8
< Content-Length: 33944
< Connection: keep-alive
< X-Backend: MYSQL
< X-Cache-Status: MISS
< Backend: book
< 
<Curl/7.68.0IPPhoneDirectory>
    <Title>Restfs address book</Title>
    <Prompt>Prompt text.</Prompt>
     <DirectoryEntry>
        <Name></Name>
        <Telephone>240001</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name>remote office</Name>
        <Telephone>240006</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name></Name>
        <Telephone>240007</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name>Svetlana</Name>
        <Telephone>240100</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name>Aleksandr</Name>
        <Telephone>240101</Telephone>
     </DirectoryEntry>
...
</Curl/7.68.0IPPhoneDirectory>
* Connection #0 to host 192.168.1.21 left intact

CODE

Пример ошибки запроса к отутствующему домену:

sasha@bsk2:~$ curl 'http://192.168.1.21:9990/error?host=book&domain=biysk&limit=3'
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.14.0 (Ubuntu)</center>
</body>
</html>

Пример ошибки когда сервис не настроен, например LDAP:

sasha@bsk2:~$ curl 'http://192.168.1.21:9990/ldap?host=book&domain=biysk&limit=3'
<html>
<head><title>500 Internal Server Error</title></head>
<body bgcolor="white">
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx/1.14.0 (Ubuntu)</center>
</body>
</html>

В логе причина - "Can't contact LDAP server":

sasha@ecss1:/var/log/ecss/restfs$ cat error-card-ldap.log 
2021/08/27 14:27:17 [error] 5907#5907: *14124 lua entry thread aborted: runtime error: /usr/lib/ecss/ecss-restfs/lua/card-ldap.lua:84: Can't contact LDAP server
stack traceback:
coroutine 0:
	[C]: in function 'assert'
	/usr/lib/ecss/ecss-restfs/lua/card-ldap.lua:84: in function </usr/lib/ecss/ecss-restfs/lua/card-ldap.lua:1>, client: 127.0.0.1, server: ecss-restfs-card-backend, request: "GET /ldap?host=book&domain=biysk&limit=3 HTTP/1.0", host: "127.61.0.1:9991"

Примеры запросов адресной книги

Запрос по общему шаблону:

sasha@bsk2:~$ curl 'http://192.168.1.21:9990/mysql?host=book&domain=biysk.local&user_agent=yealink&skip_no_disp=true&translit=false&limit=5'
<YealinkIPPhoneDirectory>
    <Title>Restfs address book</Title>
    <Prompt>Prompt text.</Prompt>
     <DirectoryEntry>
        <Name>remote office</Name>
        <Telephone>240006</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name>Светлана</Name>
        <Telephone>240100</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name>Александр</Name>
        <Telephone>240101</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name>GPON</Name>
        <Telephone>240462</Telephone>
     </DirectoryEntry>
     <DirectoryEntry>
        <Name>Электрики</Name>
        <Telephone>240464</Telephone>
     </DirectoryEntry>
</YealinkIPPhoneDirectory>
CODE

Пример запроса по шаблону для ТА GrandStream:

sasha@bsk2:~$ curl 'http://192.168.1.21:9990/mysql?host=book&domain=biysk.local&user_agent=grandstream&skip_no_disp=true&translit=false&limit=3'
<?xml version="1.0" encoding="UTF-8"?>
<AddressBook>
    <version>1</version>
    <Contact>
        <FirstName searchName="remote office">remote office</FirstName>
        <MiddleName searchName="undefined">undefined</MiddleName>
        <LastName searchName="undefined">undefined</LastName>
        <!-- phone -->
        <Phone>
            <phonenumber>240006</phonenumber>
            <accountindex>1</accountindex>
        </Phone>
        <!-- phone end -->
        <Group>0</Group>
        <PhotoUrl/>
        <RingtoneUrl>./</RingtoneUrl>
        <RingtoneIndex>0</RingtoneIndex>
        <!-- mail -->
        <!-- mail end -->
    </Contact>
    <Contact>
        <FirstName searchName="Светлана">Светлана</FirstName>
        <MiddleName searchName=""></MiddleName>
        <LastName searchName=""></LastName>
        <!-- phone -->
        <Phone>
            <phonenumber>240100</phonenumber>
            <accountindex>2</accountindex>
        </Phone>
        <!-- phone end -->
        <Group>0</Group>
        <PhotoUrl/>
        <RingtoneUrl>./</RingtoneUrl>
        <RingtoneIndex>0</RingtoneIndex>
        <!-- mail -->
        <Mail>sv@email.loc</Mail>
        <!-- mail end -->
    </Contact>
    <Contact>
        <FirstName searchName="Александр">Александр</FirstName>
        <MiddleName searchName="middle">middle</MiddleName>
        <LastName searchName="last">last</LastName>
        <!-- phone -->
        <Phone>
            <phonenumber>240101</phonenumber>
            <accountindex>3</accountindex>
        </Phone>
        <!-- phone end -->
        <Group>0</Group>
        <PhotoUrl/>
        <RingtoneUrl>./</RingtoneUrl>
        <RingtoneIndex>0</RingtoneIndex>
        <!-- mail -->
        <Mail>asz@email.loc</Mail>
        <Mail>asz@sibnet.ru</Mail>
        <!-- mail end -->
    </Contact>
</AddressBook>

CODE

Пример запроса по протоколу CardDAV(при запросе из браузера предлагает сохранить файл контактов - vcard.vcf)

sasha@bsk2:~$ curl 'http://192.168.1.21:9990/mysql?host=book&domain=biysk.local&user_agent=vcard&skip_no_disp=true&translit=false&limit=3'
BEGIN:VCARD
VERSION:4.0
PRODID:-//ECSS RESTFS//Carddav 3.11//EN
UID:834057494898241694630
CATEGORIES: biysk.local
FN:remote office
N:undefined;undefined;undefined;;
TEL;TYPE=WORK:240006
REV:2021-08-27 13:52:00
END:VCARD
BEGIN:VCARD
VERSION:4.0
PRODID:-//ECSS RESTFS//Carddav 3.11//EN
UID:5607334834221462834727
CATEGORIES: biysk.local
FN:Светлана
N:;;;;
EMAIL;TYPE=INTERNET:sv@email.loc
TEL;TYPE=WORK:240100
REV:2021-08-27 13:52:00
END:VCARD
BEGIN:VCARD
VERSION:4.0
PRODID:-//ECSS RESTFS//Carddav 3.11//EN
UID:6500235108102668882
CATEGORIES: biysk.local
FN:Александр
N:last;first;middle;;
EMAIL;TYPE=INTERNET:asz@email.loc
EMAIL;TYPE=INTERNET:asz@sibnet.ru
TEL;TYPE=WORK:240101
REV:2021-08-27 13:52:00
END:VCARD

CODE

Сброс кэша для сервиса телефонной книги

Сброс предназначен для принудительного обновления кэша сервиса телефонной книги. При этом происходит внеочередное обращение к БД соответствующего сервиса с целью актуализации кэша. По умолчанию обновление кэша происходит раз в час.

http://<IP>:9990/update/<service>?host=book
CODE
  • <IP> — ip-адреса сервера, где установлен пакет ecss-restfs;
  • <service> — сервис используемый для получения телефонной книги (mysql/ldap/ssw).

Синхронизация телефонной книги SIP-телефонов

Для синхронизации адресной книги с телефоном, нужно в соответствующем меню ввести http адрес запроса, который будет отправляться на SSW.

Общая схема запроса приведена выше в Таблице 1.

На телефонном аппарате в параметрах настройки удаленной телефонной книги нужно указать URL доступа к сервису с необходимыми параметрами.

Пример для ТА Yealink:

Пример для ТА Eltex VP-15:

Подробнее о загрузке и синхронизации адресной книги - в документации по конкретной модели IP телефона.