какой добавочный номер (агент) зарегистрировался в системе, и сохраняем его в переменную WHO. Если значение этой переменной NULL, отклоняем исходящий звонок. Если значение переменной не NULL, с помощью функции H0TDESK_INF0() получаем информацию об агенте и сохраняем ее в нескольких переменных канала CHANNEL. Сюда входит и контекст для обработки звонка, где выполняется переход (с помощью функции Goto()) в заданный для нас контекст (который управляет нашим исходящим доступом). Если попытаться набрать номер, не обрабатываемый нашим контекстом (или одним из промежуточных контекстов - то есть контекст international содержит переход в контекст long distance, который, в свою очередь, содержит переход в local), выполняется встроенный добавочный номер i, который воспроизводит для вызывающего абонента сообщение о невозможности такого действия и отсоединяет его:
[hotdesk_outbound]
exten => _X.,1,No0p()
exten => _X.,n,Set(L0CATI0N=${CUT(CHANNEL,/,2)})
exten => _X.,n,Set(L0CATI0N=${CUT(L0CATI0N,-,1)})
exten => _X.,n,Set(WH0=${H0TDESK_PH0NE_STATUS(${L0CATI0N})})
exten => _X.,n,GotoIf($[${ISNULL(${WH0})}]?no_outgoing,1)
exten => _X.,n,Set(${WH0}_CID_NAME=${H0TDESK_INF0(cid_name,${WH0})})
exten => _X.,n,Set(${WH0}_CID_NUMBER=${H0TDESK_INF0(cid_number,${WH0})})
exten => _X.,n,Set(${WH0}_C0NTEXT=${H0TDESK_INF0(context,${WH0})})
exten => _X.,n,Goto(${${WH0}_C0NTEXT},${EXTEN},1)
[international]
exten => _011.,1,No0p()
exten => _011.,n,Set(E=${EXTEN})
exten => _011.,n,Goto(outgoing,call,1)
exten => i,1,No0p()
exten => i,n,Playback(silence/2&sorry-cant-let-you-do-that2) exten => i,n,Hangup()
include => longdistance
[longdistance]
exten => _1NXXNXXXXXX,1,No0p()
exten => _1NXXNXXXXXX,n,Set(E=${EXTEN})
exten => _1NXXNXXXXXX,n,Goto(outgoing,call,1)
exten => _NXXNXXXXXX,1,Goto(1${EXTEN},1)
exten => i,1,No0p()
exten => i,n,Playback(silence/2&sorry-cant-let-you-do-that2) exten => i,n,Hangup()
include => local
[local]
exten => _416NXXXXXX,1,No0p()
exten => _416NXXXXXX,n,Set(E=${EXTEN})
exten => _416NXXXXXX,n,Goto(outgoing,call,1) exten => i,1,NoOp()
exten => i,n,Playback(silence/2&sorry-cant-let-you-do-that2) exten => i,n,Hangup()
Если звонок может быть выполнен, он направляется на обработку в контекст [outgoing], где с помощью функции CALLERID() задаются имя и номер для ID вызывающего абонента. После этого вызов передается по SIP-каналу с помощью service_provider, который был создан в файле sip.conf. [outgoing]
exten => call,1,NoOp()
exten => call,n,Set(CALLERID(name)=${${WHO}_CID_NAME}) exten => call,n,Set(CALLERID(number) =${${WHO}_CID_NUMBER}) exten => call,n,Dial(SIP/service_provider/${E}) exten => call,n,Playback (silence/2&pls-try-call-later) exten => call,n,Hangup()
Наш service_provider в файле sip.conf мог бы выглядеть примерно так:
[service_provider] type=friend
host=switch1.service_provider.net
username=my_username
fromuser=my_username
secret=welcome
context=incoming
canreinvite=no
disallow=all
allow=ulaw
И это все! Полностью диалплан, используемый для реализации возможности «горячих столов», можно увидеть в приложении G. Мы только что рассмотрели столько всего, что можно реализовать с помощью func_odbc! Теперь вы понимаете, почему нас так восторгает эта функция?!
Обратная совместимость func_odbc
С Asterisk 1.4 можно использовать версию func_odbc, созданную для обеспечения обратной совместимости, которая применяет немного другой формат конфигурации. Это позволяет использовать множество DSN-соединений с разными базами данных, а также применять переменную канала ${ODBCROWS} для SQL-запросов read (SELECT). Загрузить версию func_odbc для обратной совместимости и установить ее (что приведет к перезаписи существующего файла func_odbc.c) можно так:
# cd /usr/src/
# svn co http://svncommunity.digium.com/svn/func_odbc/1.4 ./func_odbc-1.4
# cp func_odbc-1.4/func_odbc.c ./asterisk-1.4/funcs
# cp: overwrite /asterisk-1.4/funcs/func_odbc.c'? y
• cd asterisk-1.4
• make install
Описанная в данной главе версия работает с текущей версией Asterisk 1.4, но в версии для обратной совместимости (и Asterisk 1.6) будет использоваться следующий измененный синтаксис:
• read станет readsql.
• write станет writesql.
• dsn станет readhandle и writehandle (для отдельных строк в базе данных для чтения и записи).
• Может быть перечислено несколько (до 5) readhandle и writehandle, в порядке предпочтения, для выполнения перехода в случае невозможности соединения с основным обработчиком.
• prefix останется неизменным.
Текущий синтаксис, используемый в Asterisk 1.4, будет работать в версии для обратной совместимости, но в следующих версиях будет приводить к формированию предупреждений о том, что этот синтаксис не рекомендуется к использованию. Со временем поддержка такого синтаксиса будет удалена.
Реализация голосовой почты с использованием ODBC
Asterisk может сохранять голосовую почту в базе данных, используя ODBC-коннектор. Это полезно в кластеризованной среде, когда требуется отделить данные голосовой почты от локальной системы, чтобы обеспечить возможность доступа к одним и тем же данным нескольким серверам Asterisk. Конечно, необходимо учесть, что в этом случае происходит централизация части Asterisk и необходимо предпринять меры для защиты этих данных, такие как регулярное резервное копирование и, возможно, кластеризация серверной части базы данных с помощью дублирования. Для PostgreSQL существует несколько хороших проектов, реализующих это: PGcluster
Asterisk хранит голосовую почту в большом двоичном объекте (Binary Large Object), или BLOB. При извлечении данных она извлекает информацию из BLOB и временно сохраняет ее на жестком диске, пока сообщение воспроизводится для пользователя. Затем, когда пользователь удаляет сообщение голосовой