#!/bin/sh # Dialplans (extensions.conf) # Implicit: ast_add_conf dialplan init_dialplanconf() { ast_enable_type dialplangeneral ast_enable_type dialplan ast_enable_type dialplanexten ast_enable_type dialplangoto ast_enable_type dialplansaytime ast_enable_type dialzone ast_enable_type inzone ast_enable_type incominggeneral ast_enable_type incoming dialplan_allowtransfer=no dialplan_dialtimeout=30 dialplan_answerfirst=no dialplan_static=yes dialplan_writeprotect=no dialplan_canreinvite=no dialplan_includes= dialplan_globals= return 0 } dialplangeneral_list="static writeprotect canreinvite clearglobalvars" dialplangeneral_list_ex="lastdialed lastdialedtype voiceboxext answerfirst dialtimeout allowtransfer international internationalout" valid_dialplangeneral() { for i in ${dialplangeneral_list} ${dialplangeneral_list_ex} ; do [ "$i" == "$1" ] && return 0 done return 1 } check_add_context() { local context="${1}" local check="${context#macro-}" [ "${context}" == ${check} ] || check="macro__${check}" eval "local isadded=\"\${dialplan_add_context_${check}-0}\"" if [ "$isadded" != "1" ] ; then eval "dialplan_add_context_${check}=1" append dialplan_contexts "$context" return 0 else return 1 fi } append_dialplan_context() { local context="${1}" local check="${context#macro-}" [ "${context}" == ${check} ] || check="macro__${check}" append dialplan_context_${check} "${2}" "${N}" } reload_dialplan() astcmd "dialplan reload" # Voicemail enable_voicemail() { enable_module res_adsi enable_module app_voicemail enable_format gsm } add_dialplan_exten() { local context=$1 logdebug 3 "Exten: $2" local ext="exten => $2," local planopt= local timeout=${dialplan_dialtimeout} # Answer extensions first. local answerfirst=${dialplan_answerfirst} local mailbox=$4 [ -z "$5" ] || timeout=$5 [ -z "$6" ] || answerfirst=$6 check_add_context "$context" if [ "$dialplan_allowtransfer" == "yes" ] ; then planopt=${planopt}t fi if [ ! -z "${planopt}" ] ; then planopt=",${timeout},${planopt}" elif [ ! -z "${timeout}" ] ; then planopt=",${timeout}" fi local dial="Dial($3$planopt)" local item="1," if [ "$answerfirst" == "yes" ] ; then append_dialplan_context ${context} "${ext}1,Answer" item="n," fi append_dialplan_context ${context} "${ext}${item}${dial}" if [ ! -z "${mailbox}" ] ; then enable_voicemail append_dialplan_context ${context} "${ext}n,VoiceMail(${mailbox})" fi append_dialplan_context ${context} "${ext}n,Congestion" } add_dialplan_include() { local context=$1 logdebug 1 "Adding Dialplan Include $1 $2" check_add_context "$context" split_append dialplan_context_${context} "include => " "$2" "${N}" } add_dialplan_saytime() { local context=$1 logdebug 1 "Adding Dialplan saytime $1 $2" check_add_context "$context" local ext="exten => $2," if [ "$dialplan_add_context_saytime" != 1 ] ; then append dialplan_contexts saytime " " dialplan_add_context_saytime=1 enable_format gsm enable_module app_sayunixtime local zone=${asterisk_zone} [ ! -z "$3" ] && zone="$3" local format="IMp AdbY" [ ! -z "$4" ] && format="$4" append dialplan_context_saytime "exten => s,1,SayUnixTime(,${zone},${format})" "${N}" fi append_dialplan_context ${context} "${ext}1,Goto(saytime,s,1)" } add_dialplan_goto() { local context=$1 logdebug 1 "Adding Dialplan goto $1 $2 $3" check_add_context "$context" append dialplan_context_${context} "exten => $2,1,Goto($3,\${EXTEN},1)" "${N}" } handle_inzone() { # TODO - Incoming zones. return 0 } #generate_inzone() { #[local_Vista] #exten => _X.,1,Ringing() #exten => _X.,n,Dial(SIP/Nokia,5) #exten => _X.,n,Answer #exten => _X.,n,wait(1) #exten => _X.,n,Set(EXITCONTEXT=xyzzy) #exten => _X.,n,Dial(SIP/Nokia,15,rd) #exten => _X.,n,Hangup #[xyzzy] #exten => 1,1,Noop(xyzzy) #exten => 1,n,SayAlpha(b) #exten => 1,n,Hangup #} append_dialplan_dialzone() { local file=$1 # Add the dialzone contexts logdebug 1 "Dialplan: Add the dialzone contexts" for zonename in ${dzones_match} ; do eval "local diallist=\${dzone_${zonename}_match-}" echo "${N}[${zonename}]" >> $file eval "dialz=\${dzone_match_use_${zonename}-}" for v in prefix internationalprefix alwaysinternational countrycode ; do eval "local $v=\${target_${v}_${dialz}:-}" eval logdebug 3 "\"${v} = '\${$v}'\"" done for v in localzone addprefix localprefix; do eval "local $v=\${dzone_${zonename}_${v}:-}" done while [ ! -z "$diallist" ] ; do cur=${diallist%%,*} nvar=${diallist#*,} if [ "${alwaysinternational}" = "yes" ] ; then # Always dial international number with this target # remove 'localprefix' (usually 0) from 'addprefix' logdebug 3 "Removing ${localprefix} from ${addprefix}" addprefix=${addprefix#$localprefix} local curlen=`expr length "${localprefix}"` if [ $curlen != 0 ] ; then # remove 0 (or local prefix) echo "exten => _${localprefix}${cur},1,Goto(${dialz}_dial,${countrycode}${addprefix}\${EXTEN:$curlen},1)" >> $file fi echo "exten => _${cur},1,Goto(${dialz}_dial,${countrycode}${addprefix}\${EXTEN},1)" >> $file else echo "exten => _${cur},1,Goto(${dialz}_dial,${addprefix}\${EXTEN},1)" >> $file fi [ "$nvar" == "$diallist" ] && break diallist=${nvar} done eval "local diallist=\${dzone_${zonename}_international-}" if [ ! -z "${diallist}" ] ; then logdebug 2 "International: ${diallist}" while [ ! -z "$diallist" ] ; do cur=${diallist%%,*} nvar=${diallist#*,} local curlen=`expr length ${cur}` if [ "$alwaysinternational" = "yes" ] ; then echo "exten => _${cur},1,Goto(${dialz}_dial,${addprefix}\${EXTEN:${curlen}},1)" >> $file else echo "exten => _${cur}.,1,Goto(${zonename}_check,${addprefix}\${EXTEN:${curlen}},1)" >> $file fi [ "$nvar" == "$diallist" ] && break diallist=${nvar} done if [ "$alwaysinternational" != "yes" ] ; then # Check for local country code echo "[${zonename}_check]" >> $file local locallen=`expr length ${countrycode}` echo "exten => _${countrycode}X.,1,Goto(${localzone-default},${localprefix-0}\${EXTEN:${locallen}},1)" >> $file echo "exten => _X.,1,Goto(${dialz}_dial,${internationalprefix}\${EXTEN},1)" >> $file fi fi done logdebug 1 "Dialplan: Finish the dialzone contexts" } append_dialplan_dialzone_out(){ local file=$1 # Add the dialzone target contexts (dialing out) logdebug 1 "Dialplan: Add the dialzone target contexts" for contype in SIP IAX ; do eval local conlist=\${dzones_${contype}-} logdebug 1 "Adding ${contype} targets: ${conlist}" for conname in $conlist ; do echo "${N}[${contype}_${conname}_dial]" >> $file for v in prefix internationalprefix alwaysinternational countrycode timeout lastdialed lastdialedtype ; do eval "local $v=\${target_${v}_${contype}_${conname}:-}" done if [ -z "${lastdialed}" ] ; then lastdialed=${dialplan_lastdialed} lastdialedtype=${dialplan_lastdialedtype} fi # [ -z "${lastcallout}" ] && lastcallout=${feature_lastcall_outqueue} # if [ ! -z "${lastcalloutexten}" ] ; then # eval "local added=\${lastcallout_outqueue_extension_${lastcalloutexten}}" # if [ ! "${added}" == 1 ] ; then # add_dialplan_lastcall extensions "${lastcalloutexten}" "${lastcallout}" "${feature_lastcall_outcount}" # eval "local lastcallout_outqueue_extension_${lastcalloutexten}=1" # fi # fi local ext="exten => _X.," local en="1," # Do not use prefix unles 'alwaysinternational' is yes [ "$alwaysinternational" != "yes" ] && internationalprefix= # if [ ! -z "${lastcallout}" ] ; then # Add lastcall out # enable_lastcall # echo "${ext}${en}Macro(lastcallstore,${internationalprefix}\${EXTEN},${lastcallout},${feature_lastcall_outcount})" >> $file # en="n," # fi if [ ! -z "${lastdialed}" ] ; then enable_module func_callerid local lastparam="\${EXTEN}" local lastmacro=${lastdialed} local lastmacrotype=${lastdialedtype} [ -z ${lastdialedtype} ] && lastdialedtype=goto local jumpmacroline= gen_jumpmacro jumpmacroline "${ext}${en}" "" "${lastmacrotype}" "${lastmacro}" "${lastparam}" echo ${jumpmacroline} >> $file en="n," fi echo "${ext}${en}Dial(${contype}/${prefix}${internationalprefix}\${EXTEN}@${conname},$timeout)" >> $file echo "exten => _X.,n,Congestion" >> $file done done } gen_jumpmacro() { logdebug 3 "Gen JumpMacro /$1/=/$2/$3/$4/$5/$6/" local leader=$2 local condition=$3 local macrotype=$4 local name=$5 local param=$6 if [ -z "${condition}" ] ; then local cond="(" else local cond="If(${condition}?" fi case ${macrotype} in gosub) enable_module app_stack # for gosub logdebug 1 "${1}=\"\${leader}Gosub\${cond}\${name},\${param},1)\"" eval "${1}=\"\${leader}Gosub\${cond}\${name},\${param},1)\"" ;; gosub_s) enable_module app_stack # for gosub logdebug 1 "${1}=\"\${leader}Gosub\${cond}\${name},s,1(\${param}))\"" eval "${1}=\"\${leader}Gosub\${cond}\${name},s,1(\${param}))\"" ;; macro) enable_module app_macro logdebug 1 "${1}=\"\${leader}Macro\${cond}\${name},\${param})\"" eval "${1}=\"\${leader}Macro\${cond}\${name},\${param})\"" ;; s|start) enable_module app_goto logdebug 1 "${1}=\"\${leader}Goto(\${iz_target},s,1)\"" eval "${1}=\"\${leader}Goto(\${iz_target},s,1)\"" ;; *) enable_module app_goto logdebug 1 "${1}=\"\${leader}Goto\${cond}\${name},\${param},1)\"" eval "${1}=\"\${leader}Goto\${cond}\${name},\${param},1)\"" ;; esac } append_jumpmacro(){ local context=$1 local jumpmacroline="" gen_jumpmacro "jumpmacroline" "$2" "$3" "$4" "$5" "$6" append_dialplan_context ${context} "${jumpmacroline}" } append_dialplan_incoming(){ local file=$1 # Evaluate the incoming ringing dialplans logdebug 1 "Add the 'incoming' dialplans: ${dialplan_extensions_incoming}" for context in ${dialplan_extensions_incoming} ; do eval "local curext=\"\${dialplan_incoming_$context}\"" logdebug 2 "Adding incoming ${curext}" check_add_context "$context" # lastcall lastcallexten lastmissed lastmissedexten for i in answerfirst beforeanswer timeout menucontext \ lastcall lastcalltype missed missedtype allowtransfer mailbox match matchcaller aftertimeout aftertimeouttype; do eval "local iz_$i=\"\${incoming_${context}_$i}\"" eval "logdebug 1 \"Incoming \$context: iz_\$i=\${iz_$i}\"" done [ ! -z ${iz_menucontext} ] && iz_answerfirst=yes if [ ! -z ${curext} ] ; then [ -z ${iz_answerfirst} ] && iz_answerfirst=${incoming_answerfirst} # [ -z ${iz_lastcall} ] && iz_lastcall=${feature_lastcall_inqueue} if [ -z ${iz_lastcall} ] ; then iz_lastcall=${incoming_lastcall} iz_lastcalltype=${incoming_lastcalltype} fi if [ -z ${iz_missed} ] ; then iz_missed=${incoming_missed} iz_missedtype=${incoming_missedtype} fi [ -z ${iz_mailbox} ] && iz_mailbox=${incoming_mailbox} [ -z ${iz_timeout} ] && iz_timeout=${incoming_timeout} [ -z ${iz_allowtransfer} ] && iz_allowtransfer=${incoming_allowtransfer} fi [ -z ${iz_match} ] && iz_match=_X. [ ! -z ${iz_matchcaller} ] && iz_match=${iz_match}/${iz_matchcaller} local ext="exten => ${iz_match}," local planopt= [ "${iz_allowtransfer}" == "yes" ] && planopt=${planopt}t local item="1," #append_dialplan_context ${context} "${ext}${item}Ringing()" if [ ! -z "${iz_lastcall}" ] ; then enable_module func_callerid local lastparam="\${CALLERID(num)}" local lastmacrotype="${iz_lastcalltype}" [ -z "${iz_lastcalltype}" ] && lastmacrotype=goto local lastmacro=${iz_lastcall} append_jumpmacro "${context}" "${ext}${item}" "" "${lastmacrotype}" "${lastmacro}" "${lastparam}" item="n," fi if [ ! -z "${iz_missed}" ] ; then enable_module func_callerid local missedparam="\${CALLERID(num)}" [ -z "${iz_missedtype}" ] && iz_missedtype=goto append_dialplan_context ${context} "${ext}${item}Set(lcsMissed=\${CALLERID(num)})" item="n," fi # Ring before answering if [ ! -z "${iz_beforeanswer}" -a ! -z "${curext}" ] ; then append_dialplan_context ${context} "${ext}${item}Dial(${curext},${iz_beforeanswer},${planopt})" iz_answerfirst=yes item="n," fi # Now answer if [ "$iz_answerfirst" == "yes" ] ; then append_dialplan_context ${context} "${ext}${item}Answer" item="n," fi # if [ ! -z ${iz_lastcallexten} ] ; then # enable_lastcall # add_dialplan_lastcall extensions "${iz_lastcallexten}" "${iz_lastcall}" "${feature_lastcall_incount}" # fi if [ ! -z ${curext} ] ; then if [ ! -z ${iz_menucontext} ] ; then planopt=${planopt}rd enable_module res_indications # wait so the next ring connects. append_dialplan_context ${context} "${ext}${item}Wait(1)" append_dialplan_context ${context} "${ext}n,Set(EXITCONTEXT=${iz_menucontext})" fi if [ ! -z ${planopt} ] ; then planopt=",${iz_timeout-},${planopt}" elif [ ! -z ${iz_timeout-} ] ; then planopt=",${iz_timeout-}" fi local dial="Dial(${curext}${planopt})" append_dialplan_context ${context} "${ext}n,${dial})" if [ ! -z ${iz_missed} ] ; then # It went to voicemail or was hung up - consider missed append_jumpmacro ${context} "${ext}${item}" "" "${iz_missedtype}" "${iz_missed}" "\${lcsMissed}" fi fi local need_hangup=1 # Add a goto or macro at the end if [ ! -z ${iz_target} ] ; then case ${iz_aftertimeouttype} in macro) append_dialplan_context ${context} "${ext}${item}Macro(${iz_target})";; s|start) append_dialplan_context ${context} "${ext}${item}Goto(${iz_target},s,1)" need_hangup=0;; *) append_dialplan_context ${context} "${ext}${item}Goto(${iz_target},\${EXTEN},1)" need_hangup=0;; esac fi if [ ! -z ${iz_mailbox} ] ; then enable_voicemail append_dialplan_context ${context} "${ext}n,VoiceMail(${iz_mailbox})" fi [ "${need_hangup}" = "1" ] && append_dialplan_context ${context} "${ext}n,Hangup" if [ ! -z ${iz_missed} ] ; then # Check for missed call append_jumpmacro ${context} "exten => h,1," "\$[\"\${DIALSTATUS}\" = \"CANCEL\"]" "${iz_missedtype}" "${iz_missed}" "\${lcsMissed}" #append dialplan_context_${context} \ # "exten => h,1,MacroIf(\$[\"\${DIALSTATUS}\" = \"CANCEL\"]?lastcallstore,\${lcsMissed},${iz_lastmissed},${feature_lastcall_incount})" "${N}" # [ ! -z ${iz_lastmissedexten} ] && \ # add_dialplan_lastcall extensions "${iz_lastmissedexten}" "${iz_lastmissed}" "${feature_lastcall_incount}" fi done } append_dialplan_extensions(){ local file=$1 # Evaluate the 'extension' dialplans logdebug 1 "Add the 'extension' dialplans: ${dialplan_exts}" for i in ${dialplan_exts} ; do eval "local curext=\"\${dialplan_ext_$i}\"" add_dialplan_exten extensions $i $curext done } append_include() { append dialplan_includes "#include <$1>" "$N" } create_dialplanconf() { # Add general section logdebug 1 "Dialplan: Add general section" local file=${DEST_DIR}/extensions.conf get_checksum dialplan_conf $file echo "${asteriskuci_gen}" > $file [ -z "${dialplan_includes}" ] || (echo "${dialplan_includes}${N}" >> $file) if [ ! -z "${dialplan_globals}" ] ; then echo "[globals]${N}${dialplan_globals}${N}" >> $file fi echo "[general]" >> $file for i in ${dialplangeneral_list} ; do eval "local val=\"\$dialplan_$i\"" if [ ! -z "$val" ] ; then echo "$i=$val" >> $file fi done append_dialplan_dialzone "$file" append_dialplan_dialzone_out "$file" append_dialplan_park "$file" append_dialplan_extensions "$file" append_dialplan_incoming "$file" # append_dialplan_lastcall "$file" # Add all the contexts logdebug 1 "Dialplan: Add all the contexts" for i in ${dialplan_contexts} ; do echo "${N}[$i]" >> $file [ "${i#macro-}" == "${i}" ] || i=macro__${i#macro-} eval "local curcontext=\"\${dialplan_context_$i-}\"" echo "$curcontext">> $file eval unset dialplan_context_$i done check_checksum "$dialplan_conf" "$file" || ast_dialplan_restart=1 return 0 } handle_dialplangeneral() { option_cb(){ if valid_dialplangeneral $1 $2 ; then eval "dialplan_$1=\"$2\"" else logerror "Invalid Dialplan General option: $1" fi } } handle_dialplan(){ context_name=$1 if [ -z ${context_name} ] ; then logerror "Context name required for dialplan" context_name=default fi option_cb() { logdebug 4 "dialplan ${context_name}: '$1' '$2'" case $1 in include_LENGTH) ;; include|include_ITEM*) add_dialplan_include ${context_name} $2 ;; *) lhs=$1 logdebug 4 "Add extension $lhs" if [ "$(expr $lhs : [0-9][0-9]\*)" != 0 ] ; then addtype=${2%%:*} if [ "${addtype}" == "$2" ]; then addparam= else addparam=${2#*:} fi case ${addtype} in mailbox|voice) add_dialplan_voice ${context_name} $1 $addparam ;; meetme|conf|conference) add_dialplan_meetme ${context_name} $1 $addparam ;; saytime|time) add_dialplan_saytime ${context_name} $1 $addparam ;; clock) add_dialplan_talkclock ${context_name} $1 $addparam ;; *) logerror "Unknown type '${addtype}' in dialplan ${context_name}, extension ${1}" esac else logerror "Invalid option: $1 for dialplan ${context_name}" fi esac } } handle_dialplanexten() { check_add dialplanexten option_cb() { case $1 in dialplan|extension|type|target|dialextension|voicebox|timeout|answerfirst) eval "dial_exten_$1=\"$2\"" esac } } check_add_dialplanexten() { if [ ! -z "${dial_exten_dialplan}" -a ! -z "${dial_exten_extension}" ] ; then local dialtarget=${dial_exten_type}/${dial_exten_dialextension+@}${dial_exten_dialextension}${dial_exten_target} check_add_context ${dial_exten_dialplan} add_dialplan_exten "${dial_exten_dialplan}" "${dial_exten_extension}" \ "${dialtarget}" "${dial_exten_voicebox}" "${dial_exten_timeout}" \ "${dial_exten_answerfirst}" fi for i in dialplan extension voicebox type target dialextension timeout answerfirst ; do eval "unset dial_voice_$i" done } handle_dialplansaytime() { check_add dialplansaytime option_cb() { case $1 in dialplan|extension|zone|format) eval "dial_saytime_$1=\"$2\"" esac } } check_add_dialplansaytime() { logdebug 1 "Add Saytime to $1 / $2" if [ ! -z "${dial_saytime_dialplan}" -a ! -z "${dial_saytime_extension}" ] ; then # local ext="exten => ${dial_saytime_extension}," [ -z "${dial_saytime_dialplan}" ] && dial_saytime_dialplan=default add_dialplan_saytime "${dial_saytime_dialplan}" "${dial_saytime_extension}" \ "${dial_saytime_zone}" "${dial_saytime_format}" fi for i in dialplan extension zone format; do eval "unset dial_saytime_$i" done } handle_dialzone_match() { eval "local isadded=\"\${dzone_${zone_name}-0}\"" if [ "${isadded}" != "1" ] ; then eval "dzone_${zone_name}=1" append dzones_match ${zone_name} " " fi append dzone_${zone_name}_match $1 "," } # Set up outgoing zones (match patterns) handle_dialzone() { zone_name=$1 logdebug 2 "Loading dialzone: $zone_name" option_cb(){ logdebug 2 "Dialzone $1/$2" case $1 in uses) local areatype=${2%[-/]*} local areaname=${2#*[-/]} logdebug 3 "Added: $areatype $areaname" eval "local isadded=\"\${dzone_${areatype}_${areaname}-0}\"" if [ "${isadded}" != "1" ] ; then eval dzone_${areatype}_${areaname}=1 append dzones_${areatype} "${areaname}" " " fi eval "dzone_match_use_${zone_name}=${areatype}_${areaname}" logdebug 3 "Finished uses" ;; match_LENGTH) ;; match|match_ITEM*) handle_dialzone_match "$2" ;; international_LENGTH) ;; international|international_ITEM*) eval "local isadded=\"$dzone_${zone_name}\"" if [ "${isadded}" != "1" ] ; then eval "dzone_${zone_name}=1" append dzones_match ${zone_name} " " fi append dzone_${zone_name}_international $2 "," ;; countrycode|localzone|addprefix|localprefix) eval "dzone_${zone_name}_${1}=\"$2\"" eval "y=\${dzone_${zone_name}_${1}}" ;; *) logerror "Invalid Dialzone option: $1 in zone '${zone_name}'" ;; esac } } check_add_dialplangoto() { logdebug 1 "Add Goto to $1 / $2" if [ ! -z "${dial_goto_dialplan}" -a ! -z "${dial_goto_extension}" ] ; then local ext="exten => ${dial_goto_extension}," check_add_context ${dial_goto_dialplan} append_dialplan_context ${dial_goto_dialplan} "${ext}1,Goto(${dial_goto_target})" fi for i in dialplan extension room ; do eval "unset dial_goto_$i" done } handle_dialplangoto(){ check_add dialplangoto option_cb() { case $1 in dialplan|extension|target) eval "dial_goto_$1=\"$2\"" ;; *) logerror "Invalid dialplangoto option: $1" esac } } # Options for incoming calls. valid_incoming_list="allowtransfer timeout answerfirst mailbox lastcall lastcalltype missed missedtype" valid_incoming_option() { is_in_list $1 ${valid_incoming_list} return $? } handle_incominggeneral() { option_cb() { if valid_incoming_option $1 ; then logdebug 3 "Add Incoming General option $1=$2" eval "incoming_${1}=\"$2\"" else logerror "Invalid incominggeneral option $1" fi } } handle_incoming() { incoming_context=$1 incoming_list= add_incoming_context "${incoming_context}" option_cb() { logdebug 1 "Incoming ${incoming_context} $1=$2" case $1 in lastcall|lastcalltype|missed|missedtype) eval "incoming_${incoming_context}_${1}=\"$2\"" ;; # lastcall|lastcallexten|lastmissed|lastmissedexten) # enable_lastcall # eval "incoming_${incoming_context}_${1}=\"$2\"" # ;; mailbox|allowtransfer|answerfirst|beforeanswer|timeout|menucontext|match|matchcaller|aftertimeout|aftertimeouttype) eval "incoming_${incoming_context}_${1}=\"$2\"" ;; target|target_ITEM*) append dialplan_incoming_${incoming_context} "$2" "&" ;; target_COUNT) ;; *) logerror "Invalid option $1 in incoming" ;; esac } } # vim: ts=2 sw=2 noet foldmethod=indent